Почтовый шлюз: задание со звёздочкой (Настройка самого exim'а)

Прежде чем накручивать внешние сервисы, нужно посмотреть, что можно сделать встроенными средствами самого exim'а.

Далее идут куски конфига exim'а с пояснениями по месту. В случаях, когда опыт и стандарт не совпадают - предпочтение отдавалось опыту.

Из конфига убрано всё лишнее, для удобства воспрятия. По большему счёту использовались только lookup'ы, особенности протокола, dnsbl и встроенный spf.

Пару слов насчёт dnsbl: используйте. Как бы на них не ругались, нервные клетки, сэкономленные на отстреле всякой нечисти, стоят той небольшой их части, которую вы потратите на вытаскивание особо одарённых в исключения.

Кроме того важен психологический момент: клиент сам попал в блэклист, и то что до нас не доходит его почта - соотвественно его собственный косяк.

## ---------------------------------------------------------
# общие настройки
## ---------------------------------------------------------
split_spool_directory
rfc1413_query_timeout = 0s
local_scan_timeout = 50s
disable_ipv6 = true
dns_ipv4_lookup = *
untrusted_set_sender = *
local_from_check = false
message_size_limit = 15m
ignore_bounce_errors_after = 45m

# эти настройки специфичны для вашего дистрибутива
exim_user = mailnull
exim_group = mail
trusted_users = mailnull : nobody

# на этой опции основана одна из проверок для совсем тупых ботов.
# при попытке использовать pipeline, когда мы его не анонсируем -
# сбрасывать соединение
# в postfix'е соответствует reject_unauth_pipeline
pipelining_advertise_hosts =

# не разрешать домены в виде @[ip-адрес]
# в стандарте оно есть, но никто в здравом уме ими не пользуется
allow_domain_literals = false

# настройки smtp-соединений
smtp_accept_queue_per_connection = 200
smtp_accept_max = 1000
smtp_accept_reserve = 200
smtp_accept_max_per_host = 10
smtp_reserve_hosts = +relay_from_hosts : +our_networks : +our_hosts

# как представляться в EHLO общем случае
primary_hostname = relay.example.com
# на каких адресах принимать соединения
local_interfaces = 4.3.2.1 : 192.168.15.2
# КРАЙНЕ ЖЕЛАТЕЛЬНО, чтобы в DNS PTR для первого адреса
# было прописано relay.example.com
# обратитесь к провайдеру

host_lookup = *
host_reject_connection = +include_unknown

helo_allow_chars = _
helo_try_verify_hosts = *

# уменьшает количество логов
log_selector = \
  -host_lookup_failed \
  -lost_incoming_connection \
  -queue_run

# домены, почта для которых доставляется в пределах этой машины
domainlist local_domains = relay.example.com

# spam_domains  - домены, от которых идёт только спам
# known_domains - домены, с которыми работаем постоянно
#     не включайте сюда shared-мегапомойки типа @mail.ru!
domainlist our_domains   = /etc/exim/lists/our_domains
domainlist spam_domains  = /etc/exim/lists/spam_domains
domainlist known_domains = /etc/exim/lists/known_domains

# trusted_hosts - пропустить большнство проверок для этих хостов
# relay_from_hosts - разрешить пересылку почты на внешние домены с этих адресов
hostlist our_hostnames     = relay.example.com
hostlist relay_from_hosts  = 127.0.0.1 : 192.168.15.4
hostlist our_hosts         = /etc/exim/lists/our_hosts
hostlist our_networks      = /etc/exim/lists/our_networks
hostlist trusted_hosts     = /etc/exim/lists/trusted_hosts
hostlist spam_hosts        = /etc/exim/lists/spam_hosts
hostlist helo_whitelist    = /etc/exim/lists/helo_whitelist
hostlist helo_spam_regexps = /etc/exim/lists/helo_spam_regexps

## ---------------------------------------------------------
acl_smtp_helo = check_helo
acl_smtp_rcpt = check_rcpt
acl_smtp_data = check_data

begin acl
## ---------------------------------------------------------
# различные проверки хостов по helo/ehlo
## ---------------------------------------------------------
check_helo:
  drop  message   = HELO/EHLO require by SMTP RFC
        hosts     = !+trusted_hosts
        condition = ${if eq{$sender_helo_name}{} {yes}{no}}

  drop  message   = IP address in HELO
        hosts     = !+trusted_hosts
        condition = ${if match{$sender_helo_name}{\N^\[[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\]?$\N} {yes}{no}}
        delay     = 5s

  drop  message   = localhost in HELO/EHLO
        hosts     = !+trusted_hosts
        condition = ${if match {$sender_helo_name} {\N^(127\.0\.0\.1|localhost(\.localdomain)?)$\N} {yes}{no}}
        delay     = 5s

  drop  message   = HELO should be FQDN
        hosts     = !+trusted_hosts
        condition = ${if !match{$sender_helo_name} {\N.*[A-Za-z0-9-]{2,}\.[A-Za-z]{2,6}$\N} {yes}{no}}
        delay     = 5s

  accept message  = Ignoring HELO/EHLO check, host was added to whitelist
        hosts     = +trusted_hosts
        condition = ${lookup{$sender_helo_name}wildlsearch*{/etc/exim/lists/helo_whitelist} {yes}{no}}

  drop  message   = Dont like your hostname
        hosts     = !+trusted_hosts
        condition = ${lookup{$sender_helo_name}wildlsearch*{/etc/exim/lists/helo_spam_regexps} {yes}{no}}
        delay     = 5s

  drop  message   = Using my HELO is a bad idea
       !hosts     = +trusted_hosts : +our_networks
        condition = ${if match{$sender_helo_name}{+our_hostnames} {yes}{no}}
        delay     = 5s

  # если helo/ehlo прошло проверки, но не идеально - включаем грейлист
  warn  !verify = helo
        set acl_m_greylist = 1

  accept

## ---------------------------------------------------------
# различные проверки получателей
## ---------------------------------------------------------
check_rcpt:
  # эти получатели могут получать всю почту, несмотря ни на какие проверки
  accept  local_parts = postmaster : admin
          domains     = +our_domains

  # эти хосты могут слать почту куда угодно,
  # но только от нашего имени
  accept hosts = +relay_from_hosts
        sender_domains = +our_domains

  # "наши" хосты могут слать почту внутри "наших" доменов
  # без авторизации
  accept  hosts = +our_networks : +our_hosts
        domains = +our_domains

  # различные виды блокировок особо настырных
  # сообщения намерено оставлены неинформативными
  deny  message = You are blocked
        sender_domains = +spam_domains

  deny  message = You are blocked
        hosts   = +spam_hosts

  deny  message = You are blocked
        senders = wildlsearch*@;/etc/exim/lists/spam_domain_regexps

  deny  message = You are blocked
        senders = wildlsearch*@;/etc/exim/lists/spam_emails

  reject message = You are not allowed to use submission port
         condition = ${if eq {$interface_port}{587} {yes}{no}}
        !hosts   = +relay_from_hosts

  # проверки обратного dns-имени
  # впринципе, их можно вынести в отдельный list и свернуть в одно правило,
  # но так отправляется вменяемое сообщение в отлупе
  warn  message   = Bad rev hostname (missing)
        hosts     = !+trusted_hosts
        condition = ${if eq{$sender_host_name}{} {yes}{no}}
        set acl_m_greylist = 1
        delay     = 5s

  deny  message   = Bad rev hostname (ip address, dashes)
        hosts     = !+trusted_hosts
        condition = ${if match{$sender_host_name} {[0-9]+-[0-9]+-[0-9]+-[0-9]+} {yes}{no}}
        delay     = 5s

  deny  message   = Bad rev hostname (ip address, dots)
        hosts     = !+trusted_hosts
        condition = ${if match{$sender_host_name} {\N[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\N} {yes}{no}}
        delay     = 5s

  deny  message   = Bad rev hostname (adsl, pool, ppp & etc)
        hosts     = !+trusted_hosts
        condition = ${if match{$sender_host_name} {[ax]dsl|pool|dialup|peer|dhcp|gprs|broadband|dslam|dynamicip|ppp|pptp|pppoe} {yes}{no}}
        delay     = 5s

  deny  message   = Bad rev hostname (vps)
        hosts     = !+trusted_hosts
        condition = ${if match{$sender_host_name} {vps[-\\.]?[0-9]+|\\.vps} {yes}{no}}
        delay     = 5s

  # проверки SPF - встроенная в exim, включается ключом при компиляции
  drop   message   = SPF for sender domain not allows mail from your host
         hosts     = !+trusted_hosts
         spf       = fail

  warn  message    = SPF softfail, greylisted
        hosts      = !+trusted_hosts
        spf        = softfail
        set acl_m_greylist = 1

  # дубовая проверка на подделку отправителя
  deny  message = Sender is in our domains, but host is not
        sender_domains = +our_domains
       !hosts = +our_networks : +our_hosts

  accept  hosts = : +trusted_hosts
        domains = +our_domains

  deny   message = Host is listed in $dnslist_domain
        dnslists = zen.spamhaus.org:bl.spamcop.net
           delay = 30s

  # если число несуществующих получателей больше семи - отлуп,
  # это явно какая-то автоспамилка
  deny   message       = Max $rcpt_fail_count failed recipients allowed
         condition     = ${if > {${eval:$rcpt_fail_count}}{7}{yes}{no}}
         delay         = ${eval: ($rcpt_fail_count) * 30}s
         log_message   = $rcpt_fail_count failed recipient attempts

  # кто-то пытается злоупотребить доверием
  # потом эти строчки в логе светятся в eximstats'е как новогодняя ёлка
  warn  log_message = External mail relaying from 'trusted' hosts
        hosts = +trusted_hosts
        domains = !+our_domains

  # проверять получателей сразу, методом коннекта к нашему внутреннему почтовику
  # не принимать письма, если у нас нет таких юзеров
  deny  message = No such user
        domains = +our_domains
       !verify  = recipient/callout=10s,defer_ok/no_details

  # пометить письма от доменов, с которыми мы работаем постоянно
  # учитывается антиспамом, смотри далее
  warn  remove_header  = x-known-domain
        sender_domains = +known_domains
        add_header     = X-Known-Domain: $sender_address_domain

  # место для проверки грейлистингом

  accept  domains = +local_domains
  accept  domains = +our_domains

  deny  message = relay not permitted
        delay = 10s

## ---------------------------------------------------------
# проверки тела письма
## ---------------------------------------------------------
check_data:
  # тут пока ничего нет, см дальше

## ---------------------------------------------------------
# маршрутизация
## ---------------------------------------------------------
begin routers

local_mail:
  driver = accept
  check_local_user
  domains = +local_domains
  transport = local_delivery

our_mail_server:
  driver = manualroute
  domains = +our_domains
  route_list = * mail.example.com byname
  transport = local_smtp

lookuphost:
  driver = dnslookup
  domains = ! +local_domains
  ignore_target_hosts = 127.0.0.0/8
  transport = remote_smtp
  no_more

system_aliases:
  driver = redirect
  allow_defer
  allow_fail
  data = ${lookup{$local_part}lsearch{/etc/aliases}}
  file_transport = address_file
  group = mail
  pipe_transport = address_pipe
  retry_use_local_part
  user = mailnull

userforward:
  driver = redirect
  check_ancestor
  check_local_user
  no_expn
  file = $home/.forward
  file_transport = address_file
  pipe_transport = address_pipe
  reply_transport = address_reply
  no_verify

localuser:
  driver = accept
  check_local_user
  transport = local_delivery

## ---------------------------------------------------------
# способы доставки
## ---------------------------------------------------------
begin transports

# в interface указан наш адрес в локальной сети
local_smtp:
  driver = smtp
  no_delay_after_cutoff
  interface = 192.168.15.2

# в interface указан наш внешний internet-адрес
remote_smtp:
  driver = smtp
  interface = 4.3.2.1
  no_delay_after_cutoff

local_delivery:
  driver = appendfile
  delivery_date_add
  envelope_to_add
  file = /var/mail/$local_part
  group = mail
  mode = 0660
  return_path_add

# не используется, оставлен для примера
address_pipe:
  driver = pipe
  return_output

# не используется, оставлен для примера
address_file:
  driver = appendfile
  delivery_date_add
  envelope_to_add
  return_path_add

# не используется, оставлен для примера
address_reply:
  driver = autoreply

# не используется, оставлен для примера
filter_pipe:
  driver = pipe
  return_fail_output
  user = nobody

## ---------------------------------------------------------
# настройка повторов
## ---------------------------------------------------------
begin retry

*              *    F,2h,15m; G,16h,1h,1.5; F,2d,8h

После того у нас всё минимально работает, нужно усилить защиту сервера, чтоб ни одна сволочь не пролезла.

К оглавлению, Далее: Усиливаем защиту внешними средствами