ВСЕВОЛОД СТАХОВ
Почтовая система на базе MTA exim
В этой статье пойдет речь о создании эффективного почтового сервера на базе MTA exim. Первый вопрос, который, конечно же, приходит в голову – «Почему именно exim?». Отвечают на этот вопрос по-разному, поэтому я скажу, что меня так привлекает в exim:
- логичная схема обработки почты;
- высокая скорость работы;
- удобный формат конфигурационного файла;
- широчайшие возможности по поиску каких-либо значений в файлах, СУБД, LDAP;
- встроенная поддержка smtp-аутентификации;
- небольшое число найденных уязвимостей (фактически я знаю только об одной, найденной недавно, она касалась версий exim до 4.20 включительно);
- очень большое количество возможностей, а также чрезвычайная гибкость;
- возможность полной замены sendmail (т.е. можно сделать):
ln -sf /usr/local/sbin/exim /usr/libexec/sendmail
На мой взгляд, exim является весьма и весьма удачным продуктом, не зря он используется по умолчанию в ОС Debian GNU Linux. Два больших минуса exim состоят в том, что отсутствует качественная документация на русском языке (этот недостаток я, возможно, постараюсь в скором времени исправить написанием книги, посвященной целиком и полностью этому замечательному MTA) и необходимость правки Makefile для включения тех или иных возможностей exim.
Итак, для начала подумаем, что должна содержать «идеальная» с точки зрения удобства администрирования и использования почтовая система. Сформулируем ряд требований к почтовой системе:
- простота управления пользователями;
- возможность предоставления доступа для отправки почты пользователям локальной сети и мобильным пользователям (при помощи smtp-аутентификации);
- максимальная защита от хакерских атак, вирусов и спама.
Базовой системой для установки MTA явилась FreeBSD 5.2.1, что обусловило определенные особенности установки. Перечислю весь набор программного обеспечения для организации mail-сервера:
- exim-4.34;
- MTA (mail transfer agent – агент передачи почты);
- courier-3.0.4 – imap-сервер для доступа к почте пользователей, не имеющих локальных пользовательских учетных записей (виртуальные пользователи);
- ClamAV – для поиска вирусов;
- SpamAssasin – для поиска спамерских писем;
- PostgreSQL-7.4.2 – для хранения всех данных о пользователях почтовой системы;
- 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);
Поясню назначение таблиц и полей:
- таблица accounts предназначена для хранения данных о виртуальных пользователях почтовой системы, назначение полей:
- uid – уникальный номер пользователя, имеет автоинкрементный тип;
- login – строка, содержащая имя пользователя в формате «имя@домен» для возможности доставки почты для пользователей различных доменов;
- password – пароль в открытом виде (для возможности безопасной cram-md5 аутентификации);
- maildir – путь к почтовому ящику в формате maildir -gecos – комментарий (для чего-нибудь да пригодится);
- gid – идентификатор группы (не нужен реально, но зачем-то требуется для courier);
- home – аналогично gid;
- mailquota – число в мегабайтах, указывающее квоту для пользователя;
- таблица 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 состоит из нескольких частей:
- настройка глобальных параметров;
- настройка acl’s;
- настройка роутеров;
- настройка транспортов;
- настройка аутентификаторов;
- настройка очереди;
- настройка 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
From: postmaster@test.ru
Subject: Your maildir is going full
This message is automaticaly gnerated by your mail server.
This means, that your mailbox is 75% full. If you would
override this limit new mail would not be delivered to you! "
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писок полезных ссылок:
- http://www.exim.org – сайт MTA exim, очень хорошая и подробная документация;
- ftp://ftp.exim.org/pub/exim/config.samples.tar.bz2 – примеры разнообразных конфигурационных параметров, полезно для ознакомления;
- http://duncanthrax.net/exiscan-acl – патч exiscan с сопутствующей документацией;
- http://www.clamav.net – сайт свободного антивируса ClamAV;
- http://www.spamassasin.org – официальный сайт SpamAssasin;
- http://www.courier-mta.org – ресурс courier продуктов, в том числе и courier-imap;
- http://www.squirrelmail.org – сайт web-mail-системы SquirrelMail;
- http://www.nixp.ru/cgi-bin/go.pl?q=articles;a=mta-home – exim+mutt+bmx – для домашней почтовой системы (хорошая и интересная статья);
- http://tomster.org/geek/freebsdcookbook – альтернативный вариант реализации почтовой системы, где вместо courier используется cyrus imapd;
- http://cebka.pp.ru/samag/clamstat.tar.bz2 – скрипт статистики ClamAV;
- http://cebka.pp.ru/samag/uidchk.pl – скрипт проверки пересечения uid из БД и системных.