Skip to content

Instantly share code, notes, and snippets.

@msamoylov
Created April 4, 2012 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msamoylov/31d42388cd3b7f058e22 to your computer and use it in GitHub Desktop.
Save msamoylov/31d42388cd3b7f058e22 to your computer and use it in GitHub Desktop.
Exim4 conf
######################################################################
# Runtime configuration file for Exim #
######################################################################
# Здесь мы определяем макросы, описывающие различные пути
local_interfaces = <; ::0 ; 0.0.0.0
CONFIG_PREFIX=/etc/exim4
warn_message_file = CONFIG_PREFIX/messages/warn_message_file
bounce_message_file = CONFIG_PREFIX/messages/bounce_message_file
DKIM_DOMAIN = domain.ru
DKIM_FILE = /etc/exim4/dkim/domain.ru.pem
DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}
# Здесь мы указываем, где находить наш postgresql сервер, соединение
# осуществляется через локальный сокет, команда hide помогает спрятать эту
# настройку при вызове exim -bP, когда exim выводит
# все конфигурационные опции в
# стандартный вывод. Учтите,
# что сам /usr/local/etc/exim/configure должен иметь владельца root:wheel и
# иметь права доступа 0600, что отличается от того, что принято по умолчанию
# (0644)
#hide pgsql_servers = host/database/db_user/password
hide pgsql_servers = localhost/exim/exim/exim
# Тут мы описываем списки доменов
# Local_domains включает домены, считающиеся локальными, то есть те домены, для
# которых exim делает локальную доставку, для остальных доменов почта
# доставляется по MX записям в DNS.
domainlist local_domains =${lookup pgsql{SELECT domain FROM local_domain}}
# Список хостов, почту на которые мы явно отвергаем
hostlist host_reject = ${lookup pgsql{SELECT domain FROM hostreject}}
#Список доменов куда разрешен прием
domainlist relay_to_domains = ${lookup pgsql{SELECT hosts FROM relaytohosts}}
# Список адресов, с которых разрешена передача почты во внешний мир
#hostlist relay_from_hosts =${lookup pgsql{SELECT hosts FROM relayfromhosts}}
hostlist relay_from_hosts = localhost:127.0.0.0/8:192.168.0.0/16
# Проверка получателя
acl_smtp_rcpt = acl_check_rcpt
# Проверка mime содержимого
acl_smtp_mime = acl_check_mime
# Проверка на спам и вирусы
acl_smtp_data = acl_check_data
# Здесь мы описываем наш антивирус
av_scanner = clamd:127.0.0.1 3310
# И spamassasin
spamd_address = 127.0.0.1 783
# Настройки пользователя и группы по умолчанию
exim_user = Debian-exim
exim_group = mail
# Никогда не осуществляем доставку под рутом - root должен быть алиасом на
# другого локального пользователя. Кстати, это _обязательное_ условие, заданное
# еще на этапе компиляции
never_users = root
# Настройки директории для очереди
spool_directory = /var/spool/exim
# Разделяем spool_directory на несколько более маленьких - аналог хеш таблицы,
# ускоряет обработку spool'а
split_spool_directory
# Пытаемся сделать соответствие прямой и обратной зоны dns для каждого хоста.
# Несколько затратно, но весьма полезно
# прописываем локалку чтобы не получать сообщения об идентификации локалки
host_lookup = !192.168.0.0/16 : *
# Убираем проверку 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 = 20M
accept_8bitmime
# Игнорируем сообщения, которые приходят нам же,
# давность которых более 12-ти часов
ignore_bounce_errors_after = 12h
# Удаляем замороженные сообщения, давность которых больше 1 дней.
timeout_frozen_after = 1d
# Настройки TLS
#tls_certificate = CONFIG_PREFIX/certs/mailed.crt
#tls_privatekey = CONFIG_PREFIX/certs/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:
# принимать сообщения которые пришли с локалхоста,
# не по TCP/IP
accept hosts = :
# разрешаем почту от корпоративных сетей
accept hosts = 127.0.0.1 : 192.168.0.0/16
# проверка на backresolv
# deny message = "No backresolv for your IP!"
# !senders = :
# condition = ${if eq{$sender_host_name}{}{yes}{no}}
# Запрещаем письма содержащие в локальной части
# символы @; %; !; /; |. Учтите, если у вас было
# `percent_hack_domains` то % надо убрать.
# Проверяются локальные домены
deny message = "Forbidden characters in the recepient name"
domains = +local_domains
local_parts = ^[.] : ^.*[@%!/|]
# Проверяем недопустимые символы для
# нелокальных получателей:
deny message = "Forbidden characters in the recepient name"
domains = !+local_domains
local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./
# Принимаем почту для постмастеров локальных доменов без
# проверки отправителя (я закомментировал, т.к. это -
# основной источник спама с мой ящик).
# accept local_parts = postmaster
# domains = +local_domains
# Запрещщаем, если невозможно проверить отправителя
# (отсутствует в списке локальных пользователей)
# У себя я это закоментил, по причине, что некоторые
# железяки (принтеры, & etc) и программы (Касперский, DrWEB)
# умеют слать почту, в случае проблем но не умеют ставить
# нужного отправителя. Такие письма эта проверка не пускает.
# require verify = sender
# Запрещщаем тех, кто не обменивается приветственными
# сообщениями (HELO/EHLO)
deny message = "HELO/EHLO should be configured"
condition = ${if eq{$sender_helo_name}{}{yes}{no}}
# Принимаем сообщения от тех, кто аутентифицировался:
# Вообще, большинство конфигов в рунете - это один и тот же
# конфиг написанный Ginger, в котором этот пункт расположен
# внизу. Но при таком расположении рубятся клиенты с adsl,
# ppp, и прочие зарезанные на последующих проверках. Но это
# жа неправильно! Этом мои пользователи из дома! Потому
# я это правило расположил до проверок.
accept authenticated = *
# Рубаем нах, тех, кто подставляет свой IP в HELO
deny message = "Your IP address should not be presented as HELO!"
hosts = *:!+relay_from_hosts
condition = ${if eq{$sender_helo_name}\
{$sender_host_address}{true}{false}}
# Рубаем тех, кто в HELO пихает мой IP
deny condition = ${if eq{$sender_helo_name}\
{$interface_address}{yes}{no}}
hosts = !127.0.0.1 : !localhost : *
message = "Don't try to cheat me with my own address"
# Рубаем тех, кто в HELO пихает только цифры
# (не бывает хостов ТОЛЬКО из цифр)
deny condition = ${if match{$sender_helo_name}\
{\N^\d+$\N}{yes}{no}}
hosts = !127.0.0.1:!localhost:*
message = "HELO can't contain numbers only!"
# Рубаем хосты типа *adsl*; *dialup*; *pool*;....
# Нормальные люди с таких не пишут. Если будут
# проблемы - уберёте проблемный пункт (у меня клиенты
# имеют запись типа asdl-1233.zone.su - я ADSL убрал...)
# deny message = "I don't like your hostname..."
# condition = ${if match{$sender_host_name} \
# {adsl|dialup|pool|peer|dhcp} \
# {yes}{no}}
# Рубаем тех, кто в блэк-листах. Серваки перебираются
# сверху вниз, если не хост не найден на первом, то
# запрашивается второй, и т.д. Если не найден ни в одном
# из списка - то почта пропускается.
deny message = "host in blacklist- $dnslist_domain \n $dnslist_text"
dnslists = opm.blitzed.org : \
proxies.blackholes.easynet.nl : \
cbl.abuseat.org : \
bl.spamcop.net : \
bl.csma.biz : \
dynablock.njabl.org :
# dnslists = :
# Задержка. (это такой метод борьбы со спамом,
# основанный на принципе его рассылки) На этом рубается
# почти весь спам. Единственно - метод неприменим на
# реально загруженных MTA - т.к. в результате ему
# приходится держать много открытых соединений.
# но на офисе в сотню-две человек - шикарный метод.
#
# более сложный вариант, смотрите в статье по exim и
# курьер имап. Т.к. там метод боле умный (просто правил
# больше :), то можно и на более загруженные сервера ставить)
warn
# ставим дефолтовую задержку в 20 секунд
set acl_m0 = 10s
warn
# ставим задержку в 0 секунд своим хостам и
# дружественным сетям (соседняя контора :))
hosts = +relay_from_hosts
set acl_m0 = 0s
# Проверка получателя в локальных доменах.
# Если не проходит, то проверяется следующий ACL,
# и если непрошёл и там - deny
accept domains = +local_domains
endpass
message = "We don't have this address here"
verify = recipient
# Проверяем получателя в релейных доменах
# Опять-таки если не проходит -> следующий ACL,
# и если непрошёл и там - deny
accept domains = +relay_to_domains
endpass
message = "I don't route this address..."
verify = recipient
# Разрешаем почту от доменов в списке relay_from_hosts
accept hosts = +relay_from_hosts
deny hosts =!+relay_from_hosts
# Реализация нашего бан-листа
deny hosts = +host_reject
message = You are banned. Go away.
# еще боримся со спамом и вирусами
deny message = We don't accept ".$found_extension". \
You should ZIP it first.
demime = bat:btm:cmd:com:cpl:dll:exe:lnk:msi:pif:prf:reg:scr:vbs:url
# Если неподошло ни одно правило - чувак явно ищет
# открытый релей. Пшёл прочь. :)
deny message = "This is not an open relay. Fuck you"
# Список доступа для проверки 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, no one speaks chinese here
condition = ${if eq{$mime_charset}{gb2312}{1}{0}}
accept
# Проверка содержимого на вирусы и спам
acl_check_data:
accept condition = ${if >={$message_size}{500k}{yes}{no}}
warn message = X-Spam-Score: $spam_score ($spam_bar)
spam = nobody:true
warn message = X-Spam-Report: $spam_report
spam = nobody:true
condition = ${if >{$spam_score_int}{0}{true}{false}}
warn message = X-Spam-Status: Yes
spam = nobody:true
condition = ${if >{$spam_score_int}{50}{true}{false}}
deny message = This message scored $spam_score spam points.
spam = nobody:true
condition = ${if >{$spam_score_int}{70}{true}{false}}
# Мы не запрещаем письма со спамом, а просто добавляем заголовок,
# содержащий количество спамерских очков, а пользователь на своей
# стороне уже просто настраивает свои фильтры. Так мы исключаем жалобы
# со стороны пользователей о потерянных письмах
#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
# Вот что-что, а вирусы нам не нужны.
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 работает даже для иерархических алиасов
# Также определяются транспорты
# для передачи почты в файл (>/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'}{$value}fail}
user = mail
group = mail
file_transport = address_file
pipe_transport = address_pipe
#routers переадресация почты
userforward:
driver = redirect
allow_fail
allow_defer
data = ${lookup pgsql{SELECT recipients FROM userforward \
WHERE local_part='${local_part}'}}
############################################################################
# Локальная доставка, если данный пользователь найдем в базе
localuser:
driver = accept
condition = ${lookup pgsql {select uid from \
accounts where login = '$local_part'}{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
dkim_domain = domain.ru
dkim_selector = dkim
dkim_private_key = /etc/exim4/dkim/domain.ru.pem
# Этот транспорт доставляет почту в локальные maildir'ы.
# Путь к maildir хранится
# опять же в таблице accounts. Разрешения на директорию 0770 для возможности
# работы с данными директориями imap сервера. При этом владельцем является
# группа и пользователь из accounts
#(потому при вставлении записей в эту таблицу
# надо начинать значения uid с достаточно большого числа, например, 2000 и
# пересекаться с реальными пользователями оно должно только если реальному
# пользователю нужен локальный доступ к maildir'у).
# Также из таблицы accounts извлекается данные о размере квоты, и
# устанавливается порог в 75% от квоты, когда пользователю посылается указанное
# предупреждение об подходе к порогу квоты
local_delivery:
driver = appendfile
directory = ${lookup pgsql{select maildir from \
accounts where login = '$local_part'}{$value}fail}
create_directory
directory_mode = 0770
maildir_format
delivery_date_add
envelope_to_add
return_path_add
group = mail
user = mail
mode = 0660
no_mode_fail_narrower
quota = ${lookup pgsql{select mailquota from \
accounts where login = '$local_part'}{$value}fail}M
quota_warn_message = "\
To: $local_part\n\
Cc: admin@domain.ru\n\
From: admin@domain.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
######################################################################
# AUTHENTICATION CONFIGURATION #
######################################################################
# Описания аутентификации
begin authenticators
# CRAM-MD5 аутентификация, требует наличия пароля в открытом виде, имя
# пользователя должно быть в формате user, как оно хранится в таблице
# accounts
lookup_cram:
driver = cram_md5
public_name = CRAM-MD5
server_secret = ${lookup pgsql {select password from accounts where login = replace('$1', '@domain.ru', '');}{$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 = replace('$1', '@domain.ru', '') and password='$2'}{yes}{no}}
server_set_id = $1
# End of Exim configuration file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment