Установка
Устанавливаем exim с поддержкой mysql:
cd /usr/ports/mail/exim
make config
[x] MYSQL Link against libmysqlclient library
make install clean
Добавляем в автозапуск:
ee /etc/rc.conf
# EXIM
exim_enable="YES"
Настройка
Пример конфигурационного файла exim — он называется configure — находится здесь: /usr/local/etc/exim/:
[14:44]root@mail# ls
configure configure.default
Конфигурационный файл configure логически поделен на секции:
- Main configuration settings
- Acl configuration
- Routers configuration
- Transports configuration
- Retry configuration
- Rewrite configuration
- Authentication configuration
- Configuration local_scan()
каждая секция — это набор изменяемых параметров, отвечающих за ту или иную область деятельности почтового сервера. Рассмотрим некоторые из них…
— Main configuration settings
Полное имя хоста FQDN:
primary_hostname = mail.pro-voip.com.ua
Создание 2-х списков доменов и 1 списка хостов, эти списки могут быть использованы далее в конфигурации при использовании, например, списков доступа acl:
domainlist local_domains = @
domainlist relay_to_domains =
hostlist relay_from_hosts = localhost
domainlist local_domains = my.first.domain : my.second.domain — список локальных доменов
можно указать, как в примере, знаком @ — что будет означать “имя локального хоста” — т.е. то. что указано в primary_hostname или определено автоматически
если не собираетесь производить локальные доставки (local delivery) можно @ удалить.
domainlist relay_to_domains = — список доменов, для которых наш хост — incoming relay
hostlist relay_from_hosts = — список хостов, для которых наш хост является исходящим рилеем, т.е. хосты из этого списка могут отправлять почту на другие хосты в инернете. Обязательно указываем localhost, чтобы наш хост смог отправлять почту, используя loopback интерфейс.
Почтовый сервер у меня будет управляться через панель Postfixadmin. Для exim’а это будет выглядеть как подключение к mysql базе postfix для извлечения оттуда полезных exim’у данных. Связка exim -> mysql (db postfix) -> postfixadmin.
### Подключение к MYSQL
# hostname / database / name / password
hide mysql_servers = localhost/postfix/postfix/postfixpassword
— указываем хост, базу, логин и пароль для подключения; поскольку пароль указывается в открытом виде, перед опцией mysql_servers устанавливаем настройку «hide», чтобы предотвратить его появление при выводе всех опций конфигурирования (команда exim -bP).
Теперь списки доменов я безу из базы postfixadmin и мне не нужно каждый раз вручную править файл configure:
### LOCAL DOMAINS
domainlist local_domains = ${lookup mysql{SELECT `domain` FROM `domain` WHERE `domain`='${domain}' AND `active`='1' AND `backupmx`='0'}}
### RELAY TO DOMAINS
domainlist relay_to_domains = ${lookup mysql{SELECT `domain` FROM `domain` WHERE `domain`='${domain}' AND `active`='1'}}
Следующие 2 опции:
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data
определяют Access Control Lists (ACLs) — списки контроля доступа; Списки, перечисленые в acl_smtp_rcpt проверяют получателя при входящем SMTP-коннекте(команда RCPT); если не сконфигурировать эту проверку, smtp-почта приниматься не будет. Списки в acl_smtp_data проверяют контент в сообщении. Сами списки будут описаны ниже в секции «ACL configuration».
Подключение антивируса ClamAV. Если exim собран с content-scanning расширением, мы можем проверять входящую почту на вирусы. Модификации нужно произвести в двух местах- здесь:
av_scanner = clamd:/var/run/clamav/clamd.sock
и в acl_check_data.
Порты, которые слушает exim:
daemon_smtp_ports = 25 : 587
— 25- стандартный smtp, 587 — «message submission»
Здесь указываем домен, который добавляется к неполным адресам:
qualify_domain = pro-voip.com.ua
— неполный адрес — тот, который не содержит часть «@domain_name». Например, «admin@provoip.com.ua» — полностью определенный адрес, а просто «admin» — логин — неполный адрес. По дефолту неполные адреса принимаются только от локальных пользователей, но это можно изменить опцией recipient_unqualified_hosts.
Пользователи exim:
exim_user = mailnull
exim_group = mail
never_users = root
never_users — это список пользователей, которым никогда не будет доставлена почта. Указывается параметром FIXED_NEVER_USERS при компиляции exim’а, встраивается в бинарник и не может быть изменен. По умолчанию — это “root”. Эта дефолтная настройка подразумевает, что мы не может отправить почту root’у так, как обычному пользователю. Но можно создать алиас и слать почту туда.
Производить reverse DNS lookup на все входящие ip-соединения. Здесь можно указать список ip, для которых производить данную проверку, а можно и отключить проверку, закомментировав строку с этим параметром:
host_lookup = *
По дефолту exim ожидает полностью определенных адресов на конверте, т.е. содержащий и локальную часть, и домен. Если вы хотите принимать почту с неполных email-адресов, можно в следующих параметрах указать, с каких хостов:
# sender_unqualified_hosts =
# recipient_unqualified_hosts =
при этом неполные адреса дополняются значением с qualify_domain.
«percent hack» как он работает? Это настройка, благодаря которой email-адреса вида x%y@domain локально рероутится в x@y. Если domain не находится в списке percent_hack_domains, x%y представляется как локальная часть:
# percent_hack_domains =
для себя применения этой штуки пока не найдено.
Когда exim не может ни доставить письмо пользователю, ни вернуть отправителю, он замораживает «freezes» доставку и оставляет в очереди до тех пор, как указано в следующих опциях.
Эта опция размораживает замороженные bounce messages через 2 дня, пробует 1 раз отправить и игнорирует ошибки отправки:
ignore_bounce_errors_after = 2d
Эта опция удаляет замороженные сообщения, которым уже неделя:
timeout_frozen_after = 7d
По умолчанию, сообщения в очереди на отправку содержатся в spool-директории, зачастую это /var/spool/exim. Exim, конечно, работает лучше, если очередя короткие, но это не всегда возможно. Если раскомментировать следующую опцию, сообщения в очереди поделятся на 62 субдиректории 0,1… A, B, … a, b, … z. Зачем это нужно? Exim может обработать по одной субдиректории в один момент, вместо целой /var/spool/exim. Это может быть полезно при больших очередях:
# split_spool_directory = true
Ограничение максимального размера письма:
message_size_limit = 50M
Логируем всё:
log_selector = +all
— ACL configuration
begin acl
— помечаем начало секции конфигурирования списков доступа acl.
Любой acl начинается с указания имени, затем идет список правил, а затем действие по умолчнаию.
Следующий acl производит проверку получателя:
acl_check_rcpt: — имя acl
Далее — набор правил:
Принять, если источник — локальный SMTP:
accept hosts = :
control = dkim_disable_verify
Добавим пару строк конфига для проверки hello:
### CHECK HELLO
drop condition = ${if eq{$sender_helo_name}{}}
message = Access denied - RFCs mandate HELO/EHLO before mail can be sent
hosts = !+relay_from_hosts
drop condition = ${if match{$sender_helo_name}{localhost}{yes}{no}}
message = Access denied - You are not localhost
hosts = !+relay_from_hosts
drop condition = ${if match{$sender_helo_name}{^[0-9]\.[0-9]\.[0-9]\.[0-9]}{yes}{no} }
message = "Dropped IP-only or IP-starting helo"
###
Сообщения, адресование локальным доменам (local_domains) и имеющие запрещенные символы в локальной части адреса — @ или % или ! или / или | или точки подряд, — не отправляются, а отправителю отсылается сообщение «Restricted characters in address»:
deny message = Restricted characters in address
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
Следующее правило похожее, но применяетя ко всем остальным доменам:
deny message = Restricted characters in address
domains = !+local_domains
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
Включаем проверку адреса отправителя:
require verify = sender
— этот оператор требует проверки адреса отправителя перед тем как проганять адрес получателя через acl. Проверка состоит из попытки зароутить адрес отправителя (по дефолту проверяется только домен).
Принимаем почту от хостов, перечисленных в списке relay_from_hosts, т.е. от хостов, для которых наш сервер является исходящим рилеем:
accept hosts = +relay_from_hosts
control = submission
control = dkim_disable_verify
Принимаем почту от всех аутентифицированных пользователей с любого хоста:
accept authenticated = *
control = submission
control = dkim_disable_verify
При такой конфигурации последних двух правил возникла следующая проблема при отправке писем — некорректные заголовки:
2014-06-16 10:07:08 [53418] 1WwR0W-000Dta-Q9 "admin@pro-voip.com.ua" from env-from rewritten as ""admin@pro-voip.com.ua"@pro-voip.com.ua" by submission mode
2014-06-16 10:07:08 [53418] 1WwR0W-000Dta-Q9 <= "admin@pro-voip.com.ua"@pro-voip.com.ua H=(admin.local) [10.10.10.100]:53382 I=[10.10.10.10]:587 P=esmtpa A=auth_cram_md5:admin@pro-voip.com.ua S=667 M8S=0 id=539E979C.9050602@pro-voip.com.ua T="test" from <admin@pro-voip.com.ua> for someuser@mail.ru
2014-06-16 10:07:08 [53419] cwd=/var/spool/exim 3 args: /usr/local/sbin/exim -Mc 1WwR0W-000Dta-Q9
2014-06-16 10:07:09 [53421] 1WwR0W-000Dta-Q9 SMTP error from remote mail server after MAIL FROM:<"admin@pro-voip.com.ua"@pro-voip.com.ua> SIZE=1707: host mxs.mail.ru [217.69.139.150]: 421 DNS problem (pro-voip.com.ua"@pro-voip.com.ua). Try again later
2014-06-16 10:07:09 [53419] 1WwR0W-000Dta-Q9 == someuser@mail.ru R=dnslookup T=remote_smtp defer (-45): SMTP error from remote mail server after MAIL FROM:<"admin@pro-voip.com.ua"@pro-voip.com.ua> SIZE=1707: host mxs.mail.ru [94.100.180.150]: 421 DNS problem (pro-voip.com.ua"@pro-voip.com.ua). Try again later
Как полечить? — отключаем submission:
accept hosts = +relay_from_hosts
### DISABLE for correct headers
# control = submission
###
control = dkim_disable_verify
accept authenticated = *
### DISABLE for correct headers
# control = submission
###
control = dkim_disable_verify
Для всех остальных рилей закрыт:
require message = relay not permitted
domains = +local_domains : +relay_to_domains
Включаем проверку адреса получателя:
require verify = recipient
Подключаем black-листы:
### DNSBL
#SPAMCOP
deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
dnslists = bl.spamcop.net
hosts = !+white_list
#SPAMHOUSE
deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
dnslists = zen.spamhaus.org
hosts = !+white_list
#CBL.ABUSEAT
deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
dnslists = cbl.abuseat.org
hosts = !+white_list
###
Действие по умолчанию после прохождения всех проверок:
accept
ACl для проверки самого сообщения (сканируем антивирусом ClamAV):
acl_check_data:
### ClamAV scan
deny !hosts = +no_av
!condition = ${if eq {${extract{3}{ }{$h_X-Virus-Scanned:}}}\
{${hmac{md5}{AVSCAN_SECRET}\
{${extract{1}{ }{$h_X-Virus-Scanned:}},${extract{2}{ }{$h_X-Virus-Scanned:}},$h_message-id:}}}\
{yes}{no} }
add_header = X-Virus-Scanned: ${primary_hostname} ${message_exim_id} \
${hmac{md5}{AVSCAN_SECRET}\
{${primary_hostname},${message_exim_id},$h_message-id:}}
malware = *
message = This message contains a virus ($malware_name).
###
accept
Routers configuration
параметры в этой секции определяют, как будут обрабатываться адреса.
Роутеры — это модули exim’а, принимающие решения о том, куда будут пересылаться сообщения.
Порядок расположения роутеров важен!!!
begin routers
— начало секции конфигурирования роутеров.
С помощью этого роутера exim маршрутизирует адреса, которые не находятся в локальных доменах, используя DNS lookup:
dnslookup:
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
no_more
domains — список доменов, к которым будет применяться роутер
dnslookup (имя роутера) и driver = dnslookup (имя драйвера) совпадают
dnslookup-роутер запрашивает у DNS MX-запись домена. MX-запись возвращает ip-адрес хоста, которому exim перенаправит письмо. Для обработки адреса этим роутером, адрес не должен находиться в локальных доменах ( ! +local_domains), домен не должен резолвится в адреса 0.0.0.0 : 127.0.0.0/8 (exim считает, что для таких доменов просто нет DNS-записи). Если DNS lookup терпит неудачу, считается, что адрес немаршрутизируемый (unrouteable).
no_more — если dnslookup потерпел неудачу, адрес последующим роутерам на обработку не передается.
Следующий роутер — system_aliases — используется для обработки алиасов локальных доменов (алиасы описаны в файле /etc/aliases):
system_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup{$local_part}lsearch{/etc/aliases}}
user = mailnull
group = mail
file_transport = address_file
pipe_transport = address_pipe
Роутер просматривает локальную часть адреса и, если находит ее в файле /etc/aliases, передает управление драйверу redirect для дальнейшей маршрутизации на соответствующие адреса. Если адресов нет (data пустое), адрес переходит в обработку следующему роутеру.
Опять-таки, список алиасов доменов беру с базы postfix и передаю на обработку драйверу redirect:
### DOMAIN ALIASES
domain_aliases:
driver = redirect
domains = ${lookup mysql{SELECT `alias_domain` FROM `alias_domain` WHERE \
`alias_domain`='${quote_mysql:$domain}' AND `active` ='1'}}
data = ${quote:$local_part}@${lookup mysql{SELECT `target_domain` FROM \
`alias_domain` WHERE `alias_domain`='${quote_mysql:$domain}'}}
Пользователями, их ящиками заведует dovecot:
### DOVECOT
mysql_aliases:
driver = redirect
allow_fail
allow_defer
data = ${lookup mysql{SELECT LCASE(`goto`) FROM `alias` WHERE \
`address`=LCASE('${quote_mysql:$local_part@$domain}') OR \
`address`=LCASE('${quote_mysql:@$domain}') AND `active`='1' \
ORDER BY SUBSTRING_INDEX(`address`,'@',1) DESC LIMIT 1}}
dovecot_user:
driver = accept
condition = ${lookup mysql{SELECT LCASE(`goto`) FROM `alias` WHERE \
`address`=LCASE('${quote_mysql:$local_part@$domain}') OR \
`address`=LCASE('${quote_mysql:@$domain}') AND `active`='1' \
ORDER BY SUBSTRING_INDEX(`address`,'@',1) DESC LIMIT 1}{yes}{no}}
transport = dovecot_delivery
###
Самый сложный роутер в дефолтной конфигурации (пока еще неясно до конца, что он делает):
userforward:
driver = redirect
check_local_user
# local_part_suffix = +* : -*
# local_part_suffix_optional
file = $home/.forward
# allow_filter
no_verify
no_expn
check_ancestor
file_transport = address_file
pipe_transport = address_pipe
reply_transport = address_reply
condition = ${if exists{$home/.forward} {yes} {no} }
Локальную часть адреса ищет среди логинов локальных пользователей. Если найден, лезет в домашнюю директорию пользователя и ищет файл .forward, в котором описаны правила перенаправлений (адреса, каналы, файлы).
Последний роутер — доставка в ящики локальных пользователей:
localuser:
driver = accept
check_local_user
# local_part_suffix = +* : -*
# local_part_suffix_optional
transport = local_delivery
cannot_route_message = Unknown user
Transports configuration
Транспорт определяет механизм доставки сообщений. Используется после того, как с помощью роутеров было выбрано направление доставки. Порядок описания транспортов не имеет значения.
begin transports
— начало секции описания транспортов.
Транспорт для доставки сообчений через smtp-соединение:
remote_smtp:
driver = smtp
Транспорт для локальной доставки:
local_delivery:
driver = appendfile
file = /var/mail/$local_part
delivery_date_add
envelope_to_add
return_path_add
group = mail
user = $local_part
mode = 0660
no_mode_fail_narrower
продолжение следует…