Сервер в кармане, или просто о сложном!

главная - Статьи - Почта - Sendmail

Настройка Sendmail

Теги: sendmail Почтовый сервер

Конфигурационный файл sendmail

Пожалуй самым сложным, с точки зрения настройки и сопровождения, из всех программных компонентов операционных систем семейства UNIX является программа sendmail. Те, кто видел хотя бы раз его конфигурационный файл, в этом нисколько не сомневаются. Такая сложность возникла не на пустом месте - она порождение условий существования в самом беспорядочном мире - мире электронной почты. Прямое редактирование конфигурационного файла программы sendmail, обычно носящего имя "sendmail.cf" (во FreeBSD этот файл располагается в каталоге "/etc/mail"), в особенности редактирование так называемых правил хранимых в нем, - задача посильная далеко немногим. Однако, необходимость подобного прямого хирургического вмешательства - явление крайне редкое. В большинстве случаев можно (и нужно) прибегнуть к помощи макропроцессора "m4(1)".Также см. построение файла sendmail.cf.

Сам по себе макропроцессор m4 не является узкоспециальной утилитой и может применяться во многих сферах, где необходима макрообработка текстовых данных. В случае с sendmail, для последнего создана богатая библиотека макросов формата m4, являющаяся штатным компонентом исходного кода самого sendmail, соответственно, поставляемая вместе с исходным кодом и позволяющая автоматизировать и упростить настройку sendmail в подавляющем большинстве случаев. Чем мы, собственно, сейчас и займемся. Подробное описание настройки sendmail при помощи m4 приведено в файле "SENDMAIL_ROOT/cf/README" (здесь SENDMAIL_ROOT - корень дерева исходного кода senmail, во FreeBSD это каталог "/usr/share/sendmail"), мы же ограничимся описанием нашего конкретного случая.

Имя файл исходного кода m4 обычно имеет суффикс ".mc", примеры готовых подобных конфигурационных файлов можно найти в каталоге "SENDMAIL_ROOT/cf/cf". Нам предстоит написать нечто подобное, свое. На синтаксисе m4 мы останавливаться не будем в силу его простоты, за комментариями же можно обратиться к странице руководства m4(1). В качестве отправной точки для файла можно воспользоваться уже готовой заготовкой для конкретной ОС из выше приведенного каталога. Для FreeBSD стандартным исходником m4 конфигурации для sendmail является файл "/etc/mail/freebsd.mc". Однако, наш конфигурационный файл достаточно сильно отличается от того, что имеется в наличии:


01: VERSIONID(`@(#)ironbox.mc,v 0.0.0 (vap@vap.org.ru) 2002/11/30')
02: OSTYPE(freebsd4)
03: DOMAIN(generic)
04: 
05: FEATURE(local_lmtp)
06: FEATURE(nocanonify)
07: FEATURE(accept_unresolvable_domains)
08: FEATURE(accept_unqualified_senders)
09: FEATURE(virtusertable)
10: FEATURE(genericstable)
11: FEATURE(masquerade_envelope)
12: FEATURE(always_add_domain)
13:
14: VIRTUSER_DOMAIN_FILE(`/etc/mail/virtusertable.domains')
15:
16: define(`SMART_HOST', `mail.infopac.ru')
17: define(`confBIND_OPTS', `-DNSRCH -DEFNAMES')
18: define(`confSERVICE_SWITCH_FILE', `/etc/mail/service.switch')
19: define(`confSMTP_MAILER', `smtp8')
20: define(`confTO_IDENT', `0')
21: define(`confTO_QUEUEWARN', `16h')
22: define(`confTO_QUEUERETURN', `8d')
23: define(`confCON_EXPENSIVE', `True')
24: define(`SMTP_MAILER_FLAGS', `e')
25:
26: MAILER(local)
27: MAILER(smtp)

N.B. В первую очередь стоит пояснить, что префиксы в каждой строке состоящие из номера, двоеточия и пробела - всего лишь нумерация строк предназначенная для удобства дальнейшего пояснения. Эта нумерация не является составной частью реального конфигурационного файла.

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

Строка 01. Содержит определение версии файла, что позволяет упростить возможное дальнейшее сопровождение. Строка не является обязательной, но ее наличие не будет лишним. Этот макрос всего лишь дополняет результирующий файл строкой с информацией о версии. Формат самой строки произволен и может содержать, к примеру, информацию о версии в формате SCCS, RCS, CVS или каком-либо ином.

Пара пояснений. Хост, на котором я работаю, носит имя "ironbox", мое имя пользователя в системе, разумеется, - "vap". Остальное понятно без комментариев.

N.B. Обратите внимание, что в макросе строка открывается обратной кавычкой [`], а закрывается прямой [']. Таков синтаксис и за этим нужно следить внимательно, поскольку сообщения об ошибке при трансляции не будет.

Строка 02. Обязательный макрос объявления типа операционной системы. Выражение стоящее в скобках определяет имя, без суффикса ".m4", подключаемого файла из каталога "SENDMAIL_ROOT/cf/ostype/". Расположенные там файлы содержат определения системно-зависимых вещей, таких как пути доступа к каталогам, файлам и программам; аргументов командных строк программ почтовиков и прочих подобных вещей. Неуказание типа операционной системы приведет к ошибке во время трансляции.

Строка 03. Определение типа почтового домена. Подключает набор макросов задающих параметры специфичные для данного почтового домена. Подключаемые файлы расположены в каталоге "SENDMAIL_ROOT/cf/domain/". Для большинства реализаций вполне подходит "generic".

На этом общая схема конфигурации заканчивается, настает очередь определения особенностей - FEATURE. Стоит отметить, что каждый компонент конфигурации должен иметь свое место в конфигурационном файле, поскольку порядок их следования важен. Полная структура и порядок следования описаны в "SENDMAIL_ROOT/cf/README".

Строка 05. Объявляем, что в нашей системе имеется локальный агент доставки поддерживающий протокол LMTP (local mail transfer protocol - локальный протокол доставки почты, описанный в RFC2033). Для FreeBSD это "mail.local(8)". Имеет ли ваша система локальный почтовик с поддержкой LMPT описано в сопроводительной документации вашей ОС. Вообще говоря, наличие LMTP почтовика не критично, но полезно. Поэтому, если убрать этот параметр, то ничего страшного не произойдет.

Строка 06. Не выполнять канонификацию адресов. Операция канонификации требует выполнения запросов к серверу DNS, что в нашем случае не желательно и мы от этого будем избавляться всеми возможными средствами.

Строки 07 и 08. Указываем sendmail принимать почтовые отправления с неопределенным доменным именем в адресе отправителя или с частично сформированным самим адресом отправителя в команде "MAIL FROM:". Это нужно по простой причине - из-за трудностей доступа к серверу DNS мы все равно не сможем выполнить проверку адресов.

Строка 09. Использование таблицы виртуальных пользователей при доставке почты. По сути - это механизм синонимизации почтовых адресов назначения работающий не только в пространстве имен пользователей как, например, файл "/etc/mail/aliases" (см. "aliases(5)"), но и в пространстве доменов. Для чего эта функциональность нужна будет объяснено ниже. Кроме того, стоит отметить, что для работоспособности этого механизма необходимы еще некоторые, описанные ниже вещи.

Строка 10. Полный функциональный аналог "virtusertable" с той лишь разницей, что этот механизм отрабатывает не в момент доставки почты, а в момент передачи локального почтового сообщения на отправку. Фактически выполняется замена неквалифицированного адреса отправителя в исходящем сообщении, например, адреса не содержащего доменного имени, или иным образом особо описанного адреса на другой адрес содержащий иное имя пользователя и/или доменную часть адреса. Замена выполняется только для адреса отправителя в заголовке сообщения. Опять же, более подробно необходимость этого будет описана чуть позже.

Строка 11. Эта директива является дополнением к строке 10. Она указывает, что подмену адреса нужно выполнять не только в заголовке, но и в теле сообщения. Тем самым адрес отправителя будет заменяться везде.

Строка 12. Функциональное дополнение к строкам 9, 10 и 11 предписывающее всегда подставлять в адрес доменную часть даже если происходит локальная доставка. Это гарантирует, что механизм подстановки основанный на доменах будет срабатывать во всех случаях.

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

Строка 14. Определение расположения файла содержащего перечень доменов для которых может отрабатываться механизм виртуализации пользователей, включенный в строке 9. Если этот файл будет отсутствовать или будет пуст, то виртуализация будет работать только на уровне имен локальных пользователей, то есть будет полностью аналогична "aliases(5)". Формат файла будет описан ниже.

Строка 16. Объявляет доменное имя "продвинутого" хоста, который собственно и будет заниматься пересылкой нашей исходящей почты. Здесь "mail.infopac.ru" должно быть заменено вами на имя почтовой системы используемого вами провайдера.

Строка 17. Небольшое дополнение для строки 6 позволяющее отключить выполнение подобных операций модулем разрешения адресов, то есть избежать обращений к DNS серверу.

Строка 18. Еще один "гвоздь в крышку гроба" DNS. Фактически здесь мы только определяем расположение и использование файла коммутатора сервисов. Посредством этого механизма мы запретим обращения к DNS сервису, что будет описано позже.

Строка 19. Мы живем в стране, где для кодирования символов используются все 8 бит байта, об этом и говорим MTA, чтобы передавал все как есть в 8-битовом кодировании, а не занимался ненужным преобразованием в 7-битный формат посредством MIME64 кодирования тела сообщения.

Строка 20. Устанавливаем тайм-аут на исполнение операции "IDENT" в 0 секунд. Это сервис практически нигде не используется, а его обработка приводит к бесполезным дополнительным задержкам при передаче почты.

Строки 21 и 22. Режим работы с очередью почтовых сообщений. Учитывая то, что отправка из очереди будет выполнена только на очередном сеансе связи, который будет может через час, а может завтра, устанавливаем тайм-аут предупреждения (строка 21) о невозможности доставки на достаточно длинный срок, здесь - 16 часов. Ну и, соответственно, возврат сообщения при невозможности его отправки (строка 22) по истечении 8 дней.

Строки 23 и 24. Запрещаем доставлять почту немедленно для так называемых дорогостоящих (expensive) агентов доставки (строка 23). Фактически это приводит к тому, что почта, которая должна доставляться агентом объявленным как дорогостоящий, будет просто устанавливаться в очередь, и там покорно ждать своего часа. Теперь, в строке 24, объявляем SMTP агента доставки как дорогостоящего. Таким образом добиваемся того, что вся локальная почта будет доставляться немедленно, а внешняя будет устанавливаться в очередь.

Строки 26 и 27. Последняя описываемая группа - группа используемых почтовиков. Это минимально необходимый и обязательный в нашем случае набор. Здесь "local" - почтовик локальной доставки, "smtp" - доставка посредством протокола SMTP через TCP/IP соединение.

На этом конфигурационный макро-файл завершен. Трансляция его в формат конфигурационного файла sendmail выполняется командой вида:

m4 -D_CF_DIR_=${CFDIR}/ ${CFDIR}/m4/cf.m4 config.mc > config.cf

Здесь ${CFDIR} - корневой каталог конфигурационной библиотеки sendmail - "SENDMAIL_ROOT/cf/", во FreeBSD это - "/usr/src/contrib/sendmail/cf". Определение каталога для "-D_CF_DIR_" должно обязательно заканчиваться "/". Вместо "config.mc" и "config.cf" должны быть реальные имена ваших файлов. Для FreeBSD определение "-D_CF_DIR_" может быть опущено, поэтому в моем случае команда имела вид:

m4 /usr/src/contrib/sendmail/cf/m4/cf.m4 ironbox.mc > ironbox.cf

Внешние файлы и сервисы

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

/etc/hosts

Для начала в файле "/etc/hosts" необходимо прописать IP адрес соответствующий доменному имени хоста объявленному в директиве строки 15 исходного файла конфигурации. В моем случае строка выглядит так:

217.77.96.6	mail.infopac.ru

Ваши значения, если только вы не используете услуги компании "Инфопак", должны, разумеется, отличаться.

Сама локальная система разрешения имен должна быть настроена на первичное использование "/etc/hosts", а именно в файле "/etc/host.conf" предложение "hosts" должно предшествовать предложению "bind". Подробнее смотри hosts(5) и host.conf(5). Сказанное касается и всех других имен хостов относительно которых требуется разрешение соответствующих им IP адресов. Это необходимо для избежания лишних запросов к внешнему DNS серверу, а следовательно позволит избежать ненужных установок коммутируемого соединения.

/etc/mail/genericstable

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

имя_пользователя	адрес_электронной_почты

Трюк состоит в следующем. Если вы будете отправлять почту через локальный MTA из своего пользовательского бюджета, то во всей исходящей корреспонденции фактический ваш адрес пользователя в системе будет заменен на указанный здесь адрес, который будет подставлен в поле "From". Если в системе несколько пользователей, то, разумеется, для каждого должно существовать свое вхождение в файле. У меня содержимое этого файла выглядит так:

vap	vap@vap.org.ru

Это гарантирует, что вся моя исходящая корреспонденция будет иметь на выходе адрес отправителя "vap@vap.org.ru".

Из данного текстового исходного файла должна быть сформирована хэш-база командой:

makemap hash /etc/mail/genericstable.db < /etc/mail/genericstable

/etc/mail/service.switch

Теперь создадим файл коммутатора сервисов. В нашем случае, в соответствии с заданным в директиве строки 18 значением, это будет файл "/etc/mail/service.switch". Этот текстовый файл должен содержать единственную строку:

hosts	files

Строка предписывает sendmail использовать только информацию из файла "/etc/hosts" при разрешении имен во время доставки почты. Более подробно использование коммутатора сервисов описано в "Sendmail Installation and Operation Guide", разделе "2.5. The Service Switch".

/etc/mail/virtusertable

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

vap@vap.org.ru	vap
vap@infopac.ru	vap

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

Из данного текстового исходного файла должна быть сформирована хэш-база командой подобной команде для файла "/etc/mail/genericstable".

Вообще говоря, функциональные возможности виртуальных таблиц для файлов "/etc/mail/genericstable" и "/etc/mail/virtusertable" значительно выше приведенных здесь. Механика их работы полностью описана в документе "SENDMAIL_ROOT/cf/README".

/etc/mail/virtusertable.domains

Механизм подстановки адресов, описанный для "/etc/mail/virtusertable", будет работать только в том случае, если сам исходный домен, для которого выполняется подстановка, объявлен как виртуальный. В нашем случае это делается посредством построчного перечисления всех этих доменов в данном текстовом файле. В моем случае он имеет вид:

vap.org.ru
infopac.ru

Хэш базы для файла генерировать не нужно.

Прочее

Создаем, если он еще не создан, файл с именами локального хоста. Нам он без надобности, но sendmail его хочет. При необходимости файл можно будет потом приметить по назначению. Для создания, от пользователя "root" выполняем:

ironbox:~# touch /etc/mail/local-host-names

Поскольку мы не будем выступать SMTP сервером, то и периодически обрабатывать почтовую очередь не имеет смысла. Поэтому нужно изменить способ запуска sendmail на загрузке системы. Для этого в файле "/etc/rc.conf" пишем:

sendmail_enable="YES"
sendmail_flags="-bd"

То есть демон будет присутствовать, но не будет обрабатывать свою очередь. Подробнее смотри sendmail(8). Завершаем работу запущенного демона командой выполняемой от пользователя "root":

ironbox:~# killall sendmail

И запускаем демона в новом режиме:

ironbox:~# /usr/sbin/sendmail -bd

/etc/mail/Makefile

Во FreeBSD, с версии, если не ошибаюсь, 4.3, в каталоге "/etc/mail" есть полезная вещица - файл "Makefile", который позволяет одним приемом получить конфигурационный файл для sendmail одновременно с генерацией всех необходимых хэш-баз. Для этого достаточно придерживаться соглашений о именовании исходных и сопутствующих файлов, держать их все в указанном каталоге, а так же сделать символическую ссылку с именем "sendmail.cf" указывающую на ".cf" файл вашей конфигурации. Например, если исходник конфигурации называется "myhost.mc", то ссылка должна быть на файл "myhost.cf". В случае если у вас все так, как здесь написано, то исполнение в каталоге "/etc/mail" команды:

make all

сделает все для вас. Подробнее об этом написано в комментариях самого "/etc/mail/Makefile".

Тестирование

Как говорится - настал момент истины. Если вы все сделали так, как здесь написано, то можно приступить к тестированию. Для начала проверим, что sendmail ведет себя так, как мы того ожидаем. Для этого запускам его в тестовом режиме:

ironbox:~# sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> 3,0 vap
canonify           input: vap
Canonify2          input: vap
Canonify2        returns: vap
canonify         returns: vap
parse              input: vap
Parse0             input: vap
Parse0           returns: vap
ParseLocal         input: vap
ParseLocal       returns: vap
Parse1             input: vap
Parse1           returns: $# local $: vap
parse            returns: $# local $: vap
> 3,0 vap@vap.org.ru
canonify           input: vap @ vap . org . ru
Canonify2          input: vap < @ vap . org . ru >
Canonify2        returns: vap < @ vap . org . ru . >
canonify         returns: vap < @ vap . org . ru . >
parse              input: vap < @ vap . org . ru . >
Parse0             input: vap < @ vap . org . ru . >
Parse0           returns: vap < @ vap . org . ru . >
ParseLocal         input: vap < @ vap . org . ru . >
ParseLocal       returns: vap < @ vap . org . ru . >
Parse1             input: vap < @ vap . org . ru . >
Recurse            input: vap
canonify           input: vap
Canonify2          input: vap
Canonify2        returns: vap
canonify         returns: vap
parse              input: vap
Parse0             input: vap
Parse0           returns: vap
ParseLocal         input: vap
ParseLocal       returns: vap
Parse1             input: vap
Parse1           returns: $# local $: vap
parse            returns: $# local $: vap
Recurse          returns: $# local $: vap
Parse1           returns: $# local $: vap
parse            returns: $# local $: vap
> 3,0 vap@infopac.ru
canonify           input: vap @ infopac . ru
Canonify2          input: vap < @ infopac . ru >
Canonify2        returns: vap < @ infopac . ru . >
canonify         returns: vap < @ infopac . ru . >
parse              input: vap < @ infopac . ru . >
Parse0             input: vap < @ infopac . ru . >
Parse0           returns: vap < @ infopac . ru . >
ParseLocal         input: vap < @ infopac . ru . >
ParseLocal       returns: vap < @ infopac . ru . >
Parse1             input: vap < @ infopac . ru . >
Recurse            input: vap
canonify           input: vap
Canonify2          input: vap
Canonify2        returns: vap
canonify         returns: vap
parse              input: vap
Parse0             input: vap
Parse0           returns: vap
ParseLocal         input: vap
ParseLocal       returns: vap
Parse1             input: vap
Parse1           returns: $# local $: vap
parse            returns: $# local $: vap
Recurse          returns: $# local $: vap
Parse1           returns: $# local $: vap
parse            returns: $# local $: vap
> 3,0 somebody@somewere.net
canonify           input: somebody @ somewere . net
Canonify2          input: somebody < @ somewere . net >
Canonify2        returns: somebody < @ somewere . net . >
canonify         returns: somebody < @ somewere . net . >
parse              input: somebody < @ somewere . net . >
Parse0             input: somebody < @ somewere . net . >
Parse0           returns: somebody < @ somewere . net . >
ParseLocal         input: somebody < @ somewere . net . >
ParseLocal       returns: somebody < @ somewere . net . >
Parse1             input: somebody < @ somewere . net . >
MailerToTriple     input: < mail . infopac . ru > somebody
	< @ somewere . net . >
MailerToTriple   returns: $# relay $@ mail . infopac . ru $: somebody
	< @ somewere . net . >
Parse1           returns: $# relay $@ mail . infopac . ru $: somebody
	< @ somewere . net . >
parse            returns: $# relay $@ mail . infopac . ru $: somebody
	< @ somewere . net . >
> ^D

Жирным выделен клавиатурный ввод. Из примера хорошо видно, что свои адреса будут доставляться локально, а чужие будут отдаваться не пересылку. В вашем случае, естественно, нужно вводить и ваши же адреса.

Не забывайте, что sendmail у нас работает в режиме "установки в очередь", а это значит, что любое принятое на доставку письмо будет именно устанавливаться в очередь на обработку, но не доставляться. Для того чтобы sendmail обработал свою очередь и выполнил доставку его нужно запустить с ключом "-q":

ironbox:~# sendmail -q

Это приведет к тому, что будет обработана накопившаяся очередь, а сообщения в ней доставлены.

Литература

  1. FreeBSD: /usr/src/contrib/sendmail/README
  2. FreeBSD: /usr/src/contrib/sendmail/doc/op/op.me
  3. FreeBSD: /usr/src/contrib/sendmail/cf/README
  4. http://www.sendmail.org/faq/

Источник: http://vap.org.ru/mail@dialup/03.shtml

Авторизуйтесь для добавления комментариев!


    забыли пароль?    новый пользователь?