Всеволод Стахов
В этой статье пойдет речь о создании эффективного почтового
сервера на базе MTA exim. Первый вопрос, который, конечно же, приходит в голову
– «Почему именно exim?». Отвечают на этот вопрос по-разному, поэтому я скажу,
что меня так привлекает в exim:
n логичная схема обработки почты;
n высокая скорость работы;
n удобный формат конфигурационного файла;
n широчайшие возможности по поиску каких-либо
значений в файлах, СУБД, LDAP;
n встроенная поддержка smtp-аутентификации;
n небольшое число найденных уязвимостей
(фактически я знаю только об одной, найденной недавно, она касалась версий exim
до 4.20 включительно);
n очень большое количество возможностей, а также
чрезвычайная гибкость;
n возможность полной замены sendmail (т.е. можно
сделать):
ln -sf
/usr/local/sbin/exim /usr/libexec/sendmail
На мой взгляд, exim является весьма и весьма
удачным продуктом, не зря он используется по умолчанию в ОС Debian GNU Linux. Два
больших минуса exim состоят в том, что отсутствует качественная документация на
русском языке (этот недостаток я, возможно, постараюсь в скором времени
исправить написанием книги, посвященной целиком и полностью этому
замечательному MTA) и необходимость правки Makefile для включения тех или иных
возможностей exim.
Итак, для начала подумаем, что
должна содержать «идеальная» с точки зрения удобства администрирования и
использования почтовая система. Сформулируем ряд требований к почтовой системе:
n простота управления пользователями;
n возможность предоставления доступа для отправки
почты пользователям локальной сети и мобильным пользователям (при помощи
smtp-аутентификации);
n максимальная защита от хакерских атак, вирусов
и спама.
Базовой системой для установки
MTA явилась FreeBSD 5.2.1, что обусловило определенные особенности установки.
Перечислю весь набор программного обеспечения для организации mail-сервера:
n exim-4.34;
n MTA (mail transfer agent – агент передачи
почты);
n courier-3.0.4 – imap-сервер для доступа к почте
пользователей, не имеющих локальных пользовательских учетных записей
(виртуальные пользователи);
n ClamAV – для поиска вирусов;
n SpamAssasin – для поиска спамерских писем;
n PostgreSQL-7.4.2 – для хранения всех данных о
пользователях почтовой системы;
n SquirrelMail – просто приятный веб-интерфейс
для почты (требует веб-сервер и php).
Конечно, можно собрать все вышеперечисленные
компоненты и вручную, но я не поклонник такого подхода. Во-первых, надо очень
четко представлять порядок сборки различных компонентов. Во-вторых, необходимо
разбираться в куче опций для связки различных библиотек. В-третьих, надо четко
представлять себе все пути и знать, каких пользователей надо добавлять. Ну и
в-четвертых, это дело очень сложно обновлять. Потому мы воспользуемся услугами
системы портов (если у вас, например, Debian GNU Linux, то нужный пакет
называется exim4-daemon-heavy).
Для этого ставим следующие порты:
databases/pogstgresql7
mail/exim
при компиляции необходимо указать следующие опции:
make WITH_PGSQL=yo
mail/p5-Mail-SpamAssassin
security/clamav
mail/courier-imap
при компиляции опять же указываем:
make WITH_CRAM=yo
WITH_POSTGRESQL=yo
mail/squirrelmail
После сборки всего нужного переходим к стадии
настройки. Тут придется затратить порядочное количество времени на настройку
всех компонентов почтовой системы, и целью этой статьи является минимизация
этого времени.
Итак, начнем с настройки СУБД PostgreSQL, как
основы для построения почтовой системы. Во-первых, Postgre SQL работает с
правами системного пользователя pgsql (обратите внимание, этот пользователь
имеет реальный shell и домашний каталог – /usr/local/pgsql). Поэтому для начала
задаем пароль для данного пользователя:
# passwd pgsql
Далее делаем su pgsql и начинаем создание базы
данных:
# su pgsql
% psql
Вводим пароль пользователя pgsql и попадаем в
командную строку SQL-запросов. Для подробного ознакомления с возможностями СУБД
советую обратиться к руководству или же одной из книг. Создаем БД:
CREATE DATABASE users;
Присоединяемся к данной БД:
\c users
Создаем таблицу пользовательских аккаунтов, а
также constraint для нее:
CREATE TABLE accounts (
uid serial NOT NULL,
login character
varying(128),
"password"
character varying(128),
maildir character
varying(255),
gecos character
varying(255),
gid integer DEFAULT 150,
home character
varying(255),
mailquota integer DEFAULT
20
);
ALTER TABLE ONLY accounts
ADD CONSTRAINT uid_k
PRIMARY KEY (uid);
ALTER TABLE ONLY accounts
ADD CONSTRAINT login_k
UNIQUE (login);
Создаем таблицу алиасов:
CREATE TABLE aliases (
mail character
varying(128) NOT NULL,
alias character
varying(128)
);
ALTER TABLE ONLY aliases
ADD CONSTRAINT mail_k
PRIMARY KEY (mail);
Поясню назначение таблиц и полей:
n таблица accounts предназначена для хранения
данных о виртуальных пользователях почтовой системы, назначение полей:
n uid – уникальный номер пользователя, имеет
автоинкрементный тип;
n login – строка, содержащая имя пользователя в
формате «имя@домен» для возможности доставки почты для пользователей различных
доменов;
n password – пароль в открытом виде (для
возможности безопасной cram-md5 аутентификации);
n maildir – путь к почтовому ящику в формате
maildir -gecos – комментарий (для чего-нибудь да пригодится);
n gid – идентификатор группы (не нужен реально,
но зачем-то требуется для courier);
n home – аналогично gid;
n mailquota – число в мегабайтах, указывающее
квоту для пользователя;
n таблица aliases представляет собой просто
замену /etc/aliases, содержит два поля – mail (исходный адрес) и alias (адрес
для перенаправления).
После этого будем считать, что PostgreSQL у нас
работает. Однако для полного использования возможностей этой СУБД лучше
почитать документацию.
Далее перейдем к настройке собственно MTA exim.
Конфигурационный файл для версии «из портов» хранится в /usr/local/etc/exim/configure.
Для начала я бы хотел пояснить значение некоторых терминов и рассказать о
базовых принципах работы exim.
Представим себе путь прохождения
любого почтового сообщения. Любое письмо состоит из так называемого конверта и
собственно данных. То, с чем мы привыкли работать в почтовых клиентах, как раз
является «данными» и не имеет к конверту никакого отношения. В конверте
описываются 2 параметра: mail from: и rcpt to:, которые указывают отправителя и
получателей соответственно. Сеанс работы с MTA может предваряться
«приветствием» – стандартным (HELO) и расширенным (EHLO). В приветствии должно
указываться FQDN клиента, хотя по ряду причин не стоит слишком строго следить
за именно FQDN, т.к. многие машины могут находиться за NAT. MTA в ответ на HELO
или EHLO сообщает о своих возможностях, обеспечивая тем самым синхронизацию
клиента и сервера. Далее exim осуществляет проверку целевых адресов на предмет
их допустимости для данного сервера. Этот этап может предваряться
аутентификацией и установкой TLS-сессии. Если пользователь прошел какой-либо из
этих этапов, ему присваиваются определенные флаги, которые в дальнейшем могут
использоваться на этапе проверки конверта. По умолчанию exim осуществляет
проверку недопустимых символов в адресах и позволяет отправлять почту только из
локальных доменов и только на локальные домены. Кроме того, при успешной
аутентификации письмо проходит и в Интернет. Для остальных писем релей
(передача писем) запрещена. Все проверки в exim4 осуществляются на основании
механизма acl (access control list – список доступа). Также может
осуществляться проверка данных (этот механизм добавляется при установке патча
exiscan, который при установке портов уже ставится). После успешного
прохождения этого этапа exim просматривает rewrite-правила для переписывания
отдельных частей конверта (может использоваться в целях перенаправления всех
писем на какой-либо смарт-хост, например). Далее письмо попадает на цепочку так
называемых роутеров, которые определяют, как именно доставлять письмо. Среди роутеров
фигурирует dnslookup, доставляющий письма на основании MX-записей в DNS,
проверка файла алиасов и .forward-файлов, ну и локальная доставка. Каждый
роутер имеет некое условие срабатывания и соответствующий транспорт. При
выполнении условия exim выбирает указанный транспорт для доставки письма, иначе
письмо проходит на следующий роутер (поэтому важен порядок описания роутеров в
конфигурационном файле). Транспорты же определяют порядок доставки письма.
Таким образом, настройка exim состоит из нескольких частей:
n настройка глобальных параметров;
n настройка acl’s;
n настройка роутеров;
n настройка транспортов;
n настройка аутентификаторов;
n настройка очереди;
n настройка rewrite-правил.
В настройке exim широко используются различные
списки (hostlist, domainlist и dnslist) и так называемые lookup, при помощи
которых exim извлекает данные из внешних источников.
Многие мои знакомые, пытаясь освоить exim,
считали lookup самой запутанной темой, потому я остановлюсь на этом вопросе
чуть подробнее. Существует два типа поиска: поиск по одному ключу (single-key)
и поиск по запросу (query). Первый используется для поиска в локальных файлах,
а второй – в различного рода базах (sql, ldap и прочее). Любые lookup могут
быть вложенными, то есть в одном поиске используются результаты другого.
Поиск на основе single key строится примерно так:
${lookup{$var_to_search}lsearch{/path/to/file}}
Особо следует отметить расставление фигурных
скобок. Если вы знакомы с функциональными языками вроде лиспа, то такой
синтаксис для вас будет привычен, иначе просто нужно считать каждую конструкцию
списком, который обрабатывает exim. Тогда все строковые и другие константы тоже
заключаются в фигурные скобки.То есть вышеприведенный пример можно
рассматривать как:
$список -> ${lookup
список} -> ${lookup {$var_to_search} driver{driver_arguments}}
При этом сам файл, в котором осуществляется
поиск, должен выглядеть так:
$var_to_search: значение
Приведу более конкретный пример – построение
списка доменов из файла на основании адреса отправителя (пример из
документации):
domainlist domains =
${lookup{$sender_host_address} lsearch{/some/file}}
при этом сам файл должен выглядеть следующим образом:
192.168.3.4: domain1 :
domain2 : ...
192.168.1.9: domain3 :
domain4 : ...
При этом поиск осуществляется по первому полю.
Отличие query-style-поиска в том, что мы явно не обозначаем, что искать,
указывая вместо этого запрос. Так выглядит типичный запрос к SQL-базе:
${lookup
driver{query}{action_if_query_succeed} {action_if_query_failed}}
Например:
domainlist domains = ${lookup
pgsql{select domains from domains where
sender='$sender_host_address'}{$value}fail}
При успешном выполнении запроса возвращается
значение из базы, иначе совершается специальное действие fail, означающее
неудачный запрос.
Несколько пугающими бывают запросы к LDAP.
Разберу их поподробнее. Указание сервера и порта LDAP выглядит следующим
образом:
ldap_default_servers =
127.0.0.1::389
Запросы к директории выглядят так:
${lookup
ldap{ldap://ldap.test.ru/dc=${domain},ou=mail, o=tehnopark?mail?sub?(&(objectClass=inetOrgPerson)
\
(mail=${local_part}@${domain}))}{$value}
fail}
Форма запроса описана в стандарте, но, думаю, ее
стоит дополнительно пояснить. В запросе мы указываем адрес LDAP-сервера
(ldap://ldap.test.ru/), basedn для поиска (dc=${domain},ou=mail,o=tehnopark),
значение, которое нужно вернуть (mail), описание запроса (также в постфиксной
форме, характерной для функциональных языков:
(&(objectClass=inetOrgPerson)(mail=${local_part}@${domain}))).
Когда требуется аутентификация на сервере, тогда
просто указываем нечто вроде:
${lookup ldap
{user="cn=manager,o=tehnopark of innovation,c=RU" pass=secret \
ldap:///o=tehnopark%20of%20innovation,c=RU?sn?sub?(cn=foo)}{$value}fail}
Заметьте необходимость замены пробелов на %20,
как того требует стандарт. Более подробно обо всем этом можно узнать на http://www.exim.org/exim-html-4.30/doc/html/spec_9.html#CHAP9.
Думаю, для дальнейшего понимания статьи этого вполне достаточно.
Итак, собственно конфигурационный файл exim.
Кое-где я добавил свои комментарии, чтобы улучшить понимание различных
директив.
##########################################################
# Runtime
configuration file for Exim #
##########################################################
# Здесь мы определяем
макросы, описывающие различные пути
CONFIG_PREFIX=/usr/local/etc/exim
ACL_PREFIX=CONFIG_PREFIX/acls
CERTDIR=CONFIG_PREFIX/certs
# Здесь мы указываем, где
находить наш PostgreSQL-сервер, соединение осуществляется через локальный
сокет, команда hide
# помогает спрятать эту
настройку при вызове exim -bP, когда exim выводит все конфигурационные опции в
стандартный вывод.
# Учтите, что сам
/usr/local/etc/exim/configure должен иметь владельца root:wheel и иметь права
доступа 0600, что отличается
# от того, что принято по
умолчанию (0644)
hide pgsql_servers =
(/tmp/.s.PGSQL.5432)/users/pgsql/pAsSwOrD
# Тут мы описываем списки
доменов Local_domains включает домены, считающиеся локальными, то есть те домены,
для которых exim
# делает локальную доставку,
для остальных доменов почта доставляется по MX записям в DNS. Обратите внимание
на дополнительные
# файлы
ACL_PREFIX/localdomains и ACL_PREFIX/hostingdomains, в которых перечислены
домены, разделенные переводом строки
# (то есть по одному домену
на каждую строку)
domainlist local_domains =
unona.test.ru : ACL_PREFIX/localdomains : ACL_PREFIX/hostingdomains
# Дополнительная настройка
domainlist hosting_domains =
ACL_PREFIX/hostingdomains
# Список хостов, почту на
которые мы явно отвергаем
hostlist host_reject =
ACL_PREFIX/hostreject
domainlist relay_to_domains =
# Список адресов, с которых
разрешена передача почты во внешний мир
hostlist relay_from_hosts =
localhost : 192.168.1.0/24 : ACL_PREFIX/relayfromhosts
# Проверка получателя
acl_smtp_rcpt =
acl_check_rcptъ
# Проверка mime содержимого
acl_smtp_mime =
acl_check_mime
# Проверка на спам и вирусы
acl_smtp_data =
acl_check_virus
# Здесь мы описываем наш
антивирус
av_scanner = clamd:127.0.0.1
3310
# И spamassasin
spamd_address = 127.0.0.1 783
# Настройки пользователя и
группы по умолчанию
exim_user = mailnull
exim_group = mail
# Никогда не осуществляем
доставку под рутом - root должен быть алиасом на другого локального
пользователя.
# Кстати, это обязательное
условие, заданное еще на этапе компиляции
never_users = root
# Настройки директории для
очереди
spool_directory =
/var/spool/exim
# Разделяем spool_directory
на несколько более маленьких, аналог хеш-таблицы, ускоряет обработку spool
split_spool_directory
# Пытаемся сделать
соответствие прямой и обратной зоны DNS для каждого хоста. Несколько затратно,
но весьма полезно
host_lookup = *
# Убираем проверку identd на
клиентской стороне. Из-за неправильно настроенных firewall это часто вызывает
# длительные тайм-ауты, кроме
того, этот сервис поднят не у многих
rfc1413_query_timeout = 0s
# Указываем кое-какие лимиты
(их назначение ясно из названия)
smtp_accept_max = 50
smtp_connect_backlog = 40
smtp_accept_max_per_host = 10
smtp_accept_queue = 22
smtp_accept_queue_per_connection
= 10
recipients_max = 16
recipients_max_reject = true
message_size_limit = 16M
accept_8bitmime
# Игнорируем сообщения,
которые приходят нам же, давность которых более 12 часов
ignore_bounce_errors_after =
12h
# Удаляем замороженные сообщения,
давность которых больше недели.
timeout_frozen_after = 7d
# Настройки TLS
tls_certificate =
CERTDIR/mailed.crt
tls_privatekey =
CERTDIR/mailed.key
tls_advertise_hosts = *
tls_verify_certificates = *
# Следующая опция
закомментирована, но весьма полезна, позволяя авторизироваться только через безопасный
ssl-канал
#auth_advertise_hosts = ${if
eq{$tls_cipher}{}{}{*}}
##########################################################
#
ACL CONFIGURATION #
# Specifies
access control lists for incoming SMTP mail #
##########################################################
begin acl
# Этот список доступа описывает проверки, осуществляемые при вызове
любой RCPT-команды
acl_check_rcpt:
# Вначале проверяем
достоверность отправителя
require verify = sender
# Принимаем соединения от
локальных MUA (то есть не через TCP/IP)
accept hosts = :
##########################################################
# Проверка соответствия
почтового адреса стандарту
deny message =
Restricted characters in address
domains = +local_domains
local_parts =
^[.] : ^.*[@%!/|]
accept domains =
+local_domains
# Здесь прописаны так
называемые dnsbl, то есть черные списки MTA с открытым релеем, мы проверяем
IP-адрес
# отправителя на соответствие
таким спискам и блокируем письмо, если отправитель был найден в таком списке
deny message =
host is listed in $dnslist_domain
dnslists =
blackholes.mail-abuse.org: dialups.mail-abuse.org: relays.mail-abuse.org:
relays.ordb.org: \
work.drbl.caravan.ru:
dul.ru:sbl.spamhaus.org
# Правило на проверку всех
почтовых адресов, кроме локальных (менее строгое)
deny message =
Restricted characters in address
domains = !+local_domains
local_parts =
^[./|] : ^.*[@%!] : ^.*/\\.\\./
##########################################################
# Принимаем почту для
пользователя postmaster локальных доменов, не взирая на отправителя
accept local_parts =
postmaster
domains =
+local_domains
# Deny unless the
sender address can be verified
accept domains =
+local_domains
endpass
verify =
recipient
# Если домен в списке
relay_to_domains, то разрешаем релей
accept domains =
+relay_to_domains
endpass
verify =
recipient
accept domains =
+hosting_domains
endpass
verify =
recipient
accept hosts =
+relay_from_hosts
# Принимаем любые соединения,
которые были успешно авторизованы
accept authenticated = *
# Реализация нашего бан-листа
deny hosts =
+host_reject
message = You
are banned. Go away.
# Запрещаем все, что не
разрешено, закрывая тем самым релей для спамеров
deny message =
relay not permitted
# Список доступа для проверки
mime-частей сообщения
acl_check_mime:
# Произодим декодирование
mime-сообщений. Полезно для дальнейшей проверки на вирусы
warn decode = default
# Можно очень быстро отсеять
сообщения, просто запретив некоторые mime-вложения, чаще всего содержащие
вирусы,
# хотя, конечно, это не
панацея
deny message =
Blacklisted file extension detected
condition = ${if match
{${lc:$mime_filename}} {\N(\.wav|\.cpl|\.pif|\.bat|\.scr|\.lnk|\.com)$\N} {1}{0}}
# Много ли у нас людей,
знающих китайский? А вот китайского спама это поубавит
deny message =
Sorry, noone speaks chinese here
condition = ${if
eq{$mime_charset}{gb2312}{1}{0}}
accept
# Проверка содержимого на
вирусы и спам
acl_check_virus:
# Мы не запрещаем письма со
спамом, а просто добавляем заголовок, содержащий количество спамерских очков, а
пользователь
# на своей стороне уже просто
настраивает свои фильтры. Так мы исключаем жалобы со стороны пользователей о
потерянных письмах
warn message =
X-Spam-Score: $spam_score ($spam_bar)
spam = nobody:true
# Добавляем заголовки,
указывающие, что письма были проверены SpamAsssasin
warn message =
X-Spam-Scanned: Yes
warn message =
X-Spam-Scanner: SpamAssassin running on mail.test.ru
# Вот что-что, а вирусы нам
не нужны
deny message =
Message rejected: virus found. Your message was successfully trashed.
Hosts =
*
Malware =
*
accept
###########################################################
#
ROUTERS CONFIGURATION #
# Specifies
how addresses are handled #
###########################################################
# THE ORDER IN WHICH THE ROUTERS ARE
DEFINED IS IMPORTANT!#
# An address is passed to
each router in turn until it is #
# accepted. #
###########################################################
begin routers
# Роутер, осуществляющий
поиск по MX-записям в DNS
dnslookup:
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
ignore_target_hosts =
0.0.0.0 : 127.0.0.0/8
no_more
# Все останльные роутеры
обслуживают доставку локальной почты. Драйвер алиасов пользователя. Обратите
внимание на lookup
# в pgsql-базе. Что
интересно, этот lookup работает даже для иерархических алиасов, например,
# postmaster -> root -> cebka -> cebka@jet.msk.su. Также определяются транспорты
для передачи почты в файл (>/path/to/file)
# и в pipe
(|/usr/local/libexec/slocal)
system_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup
pgsql{select alias from aliases where mail ='$local_part@$domain'}{$value}fail}
user = mailnull
group = mail
file_transport =
address_file
pipe_transport =
address_pipe
# Для локальных пользователей
также создаем возможность перенаправления почты через ~/.forward-файл. Если
включена директива
# allow_filter, то в
.forward-файле можно использовать язык sieve-фильтров. Для подробностей см.
документацию на www.exim.org,
# т.к. на рассмотрение этой
темы уйдет слишком много времени
userforward:
driver = redirect
check_local_user
file = $home/.forward
no_verify
no_expn
check_ancestor
# allow_filter
file_transport =
address_file
pipe_transport =
address_pipe
reply_transport =
address_reply
condition = ${if
exists{$home/.forward} {yes} {no} }
# Локальная доставка, если
данный пользователь найден в базе
localuser:
driver = accept
condition = ${lookup pgsql
{select uid from accounts where login = '$local_part@$domain'}{yes}{no}}
transport = local_delivery
cannot_route_message =
Unknown user
#############################################################
# TRANSPORTS
CONFIGURATION #
#############################################################
# ORDER DOES
NOT MATTER #
#Only one appropriate
transport is called for each delivery.#
#############################################################
begin transports
# Драйвер для доставки через
соединения с удаленными smtp-серверами
remote_smtp:
driver = smtp
# Этот транспорт доставляет
почту в локальные maildir. Путь к maildir хранится опять же в таблице accounts.
Разрешения
# на директорию 0700 для
возможности работы с данными директориями imap-сервера. При этом владельцем
является
# группа и пользователь из
accounts (потому при включении записей в эту таблицу надо начинать значения uid
с достаточно
# большого числа, например,
2000 и пересекаться с реальными пользователями оно должно, только если
реальному пользователю
# нужен локальный доступ к
maildir). Также из таблицы accounts извлекаются данные о размере квоты, и
устанавливается порог в 75%
# от квоты, когда
пользователю посылается указанное предупреждение об подходе к порогу квоты
local_delivery:
driver = appendfile
directory = ${lookup
pgsql{select maildir from accounts where login =
'$local_part@$domain'}{$value}fail}
create_directory
directory_mode = 0770
maildir_format
delivery_date_add
envelope_to_add
return_path_add
group = ${lookup
pgsql{select gid from accounts where login = '$local_part@$domain'}{$value}fail}
user = ${lookup
pgsql{select uid from accounts where login =
'$local_part@$domain'}{$value}fail}
mode = 0660
no_mode_fail_narrower
quota = ${lookup
pgsql{select mailquota from accounts where login = '$local_part@$domain'}{$value}fail}M
quota_warn_message =
"\
To: $local_part@domain\n\
From:
postmaster@test.ru\n\
Subject: Your maildir is
going full\n\
This message is
automaticaly gnerated by your mail server.\n\
This means, that your
mailbox is 75% full. If you would \n\
override this limit new
mail would not be delivered to you!\n"
quota_warn_threshold = 75%
# Транспорт, осуществляющий
доставку в pipe
address_pipe:
driver = pipe
return_output
# Транспорт, осуществляющий
доставку прямо в файл
address_file:
driver = appendfile
delivery_date_add
envelope_to_add
return_path_add
# Этот транспорт используется
для автоматического ответа на сообщения об ошибках
address_reply:
driver = autoreply
##########################################################
# RETRY
CONFIGURATION #
##########################################################
begin retry
# Настройки по умолчанию,
которые я не трогал, управляют интервалом повторной передачи сообщений
# This single retry rule applies
to all domains and all errors. It specifies retries every 15 minutes for 2
hours, then increasing
# retry intervals, starting
at 1 hour and increasing each time by a factor of 1.5, up to 16 hours, then
retries every 6 hours until
# 4 days have passed since
the first failed delivery.
# Address or Domain Error
Retries
# ----------------- -----
-------
* *
F,2h,15m; G,16h,1h,1.5; F,4d,6h
##########################################################
# REWRITE
CONFIGURATION #
##########################################################
begin rewrite
# Создаем правило по
переписыванию заголовка To: с *@test.ru на локальный smart-host -
@unona.test.ru. Это было сделано
# для локальных
пользователей, по идее с хранением пользователей в postgres уже не нужно,
показано для примера.
*@test.ru $1@unona.test.ru T
##########################################################
# AUTHENTICATION
CONFIGURATION #
##########################################################
# Описания аутентификации
begin authenticators
# CRAM-MD5-аутентификация,
требует наличия пароля в открытом виде, имя пользователя должно быть в формате
user@domain,
# как оно хранится в таблице
accounts
lookup_cram:
driver = cram_md5
public_name = CRAM-MD5
server_secret = ${lookup
pgsql {select password from accounts where login='$1'}{$value}fail}
server_set_id = $1
# LOGIN-аутентификация - не
требует хранения пароля в открытом виде, однако, по сети пароль передается в
открытом виде –
# требуется лишь выполнение
условия server_condition - $1 - имя пользователя, а $2 - пароль. LOGIN
безопасен только
# при установлении
ssl-соединения.
login:
driver = plaintext
public_name = LOGIN
server_prompts =
Username:: : Password::
server_condition =
${lookup pgsql {select login from accounts where login='$1' and
password='$2'}{yes}{no}}
server_set_id = $1
##########################################################
# CONFIGURATION
FOR local_scan() #
##########################################################
# If you have built Exim to
include a local_scan() function that contains tables for private options, you
can define
# those options here.
Remember to uncomment the "begin" line. It is commented by default
because it provokes
# an error with Exim binaries
that are not built with LOCAL_SCAN_HAS_OPTIONS set in the Local/Makefile.
# begin local_scan
# End of Exim configuration
file
Далее необходимо создать сертификат для
smtp-сервера. В ходе эксплуатации выяснилось, что некоторые клиенты, например,
The Bat!, требуют, чтобы в CN-сертификате (подробнее о сертификатах и способах
их генерации можно прочитать в моей статье из данного журнала, располагающейся
по адресу http://cebka.pp.ru/my/openssl.txt)
содержалось имя (FQDN) сервера. Например, для сервера на mail.test.ru серификат
должен содержать CN: mail.test.ru. Содержание остальных полей некритично.
Создать сертификат можно, например, так:
# openssl genrsa -out
mailed.key 2048
# openssl req -new
-x509 -key mailed.key -days 365 -out mailed.crt
При этом все поля сертификата будут запрошены.
Учтите, что файл секретного ключа не должен быть зашифрован, иначе exim не
сможет его прочитать, просто установите владельцем этого файла root:wheel и
установите права 0600. После этого можно попробовать запустить exim в режиме
отладки:
exim -bd -d+all
и понаблюдать за выводимыми сообщениями. Чтобы включить exim вместо
sendmail, выполняем следующие действия: в файле /etc/rc.conf нужны строки:
sendmail_enable="NONE"
exim_enable="YES"
# А также для spamassasin и
clamav:
clamav_clamd_enable="YES"
spamd_enable="YES"
Затем настраиваем доставку почты для локальных
приложений через exim, для чего файл /etc/mail/mailer.conf у нас должен
выглядеть следующим образом:
sendmail /usr/local/sbin/exim
send-mail /usr/local/sbin/exim
mailq /usr/local/sbin/exim
newaliases /usr/local/sbin/exim
hoststat /usr/local/sbin/exim
purgestat /usr/local/sbin/exim
После этого перейдем к настройке ClamAV: заходим
в /usr/local/etc/ и копируем clamav.conf.sample в clamav.conf и правим его
таким образом, чтобы он принимал соединения через tcp/ip, а не через локальный
сокет, для чего в clamav.conf нужны следующие строки:
TCPSocket 3310
TCPAddr 127.0.0.1
Далее копируем freshclam.conf.sample в
freshclam.conf. Этот файл указывает настройки для программы freshclam,
использующейся для обновления антивирусных баз ClamAV.

SpamAssassin работает без дополнительных
настроек.

Перейдем к следующей части – настройка courier.
Этот imap-сервер для своей работы использует несколько процессов, которые, в
свою очередь, настраиваются из различных конфигурационных файлов. В моем
примере запущено 3 процесса – authdaemon, courier-imapd и courier-imapd-ssl.
Причем во внешний мир открыт только courier-imapd-ssl. Для начала создадим
сертификат для imapd-ssl и для TLS-соединений с imapd: правим файл
/usr/local/etc/couier-imap/imapd.cnf на предмет своего dn для сертификата, для
чего в части req_dn прописываем свои настройки (не забудьте, что cn должен
совпадать с FQDN imap-сервера):
[ req_dn ]
C=RU
ST=Moskow region
L=Moskow
O=Tehnopark
OU=Internet technologies
CN=test.ru
emailAddress=postmaster@test.ru
Далее генерируем сертификат специальным скриптом:
cd /usr/local/share/courier-imap/
./mkimapdcert
После чего настроим imap-аутентификацию:
cd
/usr/local/etc/courier-imap/
cp authdaemonrc.dist
authdaemonrc
Далее в authdaemonrc заменяем список модулей для
аутентификации на:
authmodulelist="authpgsql"
Теперь courier знает, что ему нужно
аутентифицироваться через pgsql. Надо только настроить сам pgsql-модуль,
настройки которого хранятся в authpqsqlrc. Он должен содержать следующее:
# Соединяемся с СУБД через
локальный сокет (не указан адрес)
PGSQL_PORT 5432
PGSQL_USERNAME pgsql
PGSQL_PASSWORD
pAsSwOrD
PGSQL_DATABASE users
# Настройки таблицы для
аутентификации, здесь мы указываем, в каких полях таблицы хранятся различные
пользовательские данные:
PGSQL_USER_TABLE
accounts
PGSQL_CLEAR_PWFIELD password
DEFAULT_DOMAIN
test.ru
PGSQL_UID_FIELD uid
PGSQL_GID_FIELD gid
PGSQL_LOGIN_FIELD login
PGSQL_HOME_FIELD home
PGSQL_NAME_FIELD gecos
PGSQL_MAILDIR_FIELD
maildir
Перейдем к настройке непосредственно imapd:
скопириуем imapd.dist в imapd. Правим следующие строчки (для остальных настроек
вполне подходят значения по умолчанию):
# imapd слушает только на
локальном адресе, для внешних соединений мы будем использовать imapd-ssl
ADDRESS=127.0.0.1
# По умолчанию в NO, делаем в
YES, чтобы imapd мог запускаться
IMAPDSTART=YES
Этих настроек в принципе хватает. Далее копируем
imapd-ssl.dist в imapd-ssl и правим следующие строчки:
IMAPDSSLSTART=YES
После чего делаем запуск courier-imap при старте
системы. Для этого заходим в /usr/local/etc/rc.d и снова немного
попереименовываем файлы:
# mv
courier-imap-imapd-ssl.sh.sample courier-imap-imapd-ssl.sh
# mv
courier-imap-imapd.sh.sample courier-imap-imapd.sh
После этого считаем, что наша почтовая система
практически готова. Нужно только немного понастраивать SquirrelMail, для чего
заходим в /usr/local/www/squirrelmail и запускаем скрипт ./configure. В
принципе интерфейс конфигурации понятен без лишних пояснений, потому я не буду
особо останавливаться на этой проблеме (не забудьте, главное, указать imap- и
smtp-сервера). Делаем симлинк на /usr/local/www/squirrelmail из какого-либо
VirtualHost или из-под непосредственно DocumentRoot самого apache. Вот в
принципе, и все.

Есть некоторые особенности запуска SquirrelMail
при включенном safe_mode в php. Во-первых, все php-файлы и каталоги
SquirrelMail должны принадлежать тому пользователю, из-под которого работает
apache (настройка VirtualHost или глобальная, по умолчанию – www:nogroup).
Кроме того, необходимо сменить разрешения в /var/spool/squirrelmail:
# chmod 1777
/var/spool/squirrelmail/attach
# chmod 1777
/var/spool/squirrelmail/pref
Ну и владельца установить того же, что и на
/usr/local/www/squirrelmail.
Комментарии вы можете присылать на cebka@jet.msk.su.
Cписок полезных
ссылок:
1. http://www.exim.org – сайт MTA exim, очень
хорошая и подробная документация;
2. ftp://ftp.exim.org/pub/exim/config.samples.tar.bz2
– примеры разнообразных конфигурационных параметров, полезно для ознакомления;
3. http://duncanthrax.net/exiscan-acl
– патч exiscan с сопутствующей документацией;
4. http://www.clamav.net – сайт свободного
антивируса ClamAV;
5. http://www.spamassasin.org – официальный
сайт SpamAssasin;
6. http://www.courier-mta.org – ресурс
courier продуктов, в том числе и courier-imap;
7. http://www.squirrelmail.org – сайт web-mail-системы SquirrelMail;
8. http://www.nixp.ru/cgi-bin/go.pl?q=articles;a=mta-home
– exim+mutt+bmx – для домашней почтовой системы (хорошая и интересная статья);
9. http://tomster.org/geek/freebsdcookbook
– альтернативный вариант реализации почтовой системы, где вместо courier
используется cyrus imapd;
10. http://cebka.pp.ru/samag/clamstat.tar.bz2
– скрипт статистики ClamAV;
11. http://cebka.pp.ru/samag/uidchk.pl
– скрипт проверки пересечения uid из БД и системных.