Debian.pro

Блог для пользователей и администраторов Debian


exim4: ограничиваем список доменов и ящиков, которым можно отправлять письма с сервера.

Вот здесь я описывал, как настроить exim4 для отправки писем с нашего сервера. Всё бы хорошо, почта шлётся, доставляется и так далее.
Огорчает, правда, то, что с нашего сервера при такой конфигурации можно отправить письмо от имени ящика.
Устраним сиё недоразумение.

Ставим exim, как описано в прошлой статье.
В конце выбираем «Split configuration into small files: yes» — так удобнее будет.

Теперь создаём необходимые нам файлы:

root@server:~# touch /etc/exim4/allowed.domains; touch /etc/exim4/allowed.mails
root@server:~# touch /etc/exim4/conf.d/router/01_exim4-outgoing-filter

В файл /etc/exim4/conf.d/router/01_exim4-outgoing-filter пишем правила, которые будут применяться к исходящей почте:

check_outgoing_from_header:
    driver = redirect
    domains = ! +local_domains
    condition = ${if !match {$header_from:}{$sender_address}}
    allow_fail
    data = :fail: You can not send mail from here with From: $header_from as sender: $sender_address

check_outgoing:
    driver = redirect
    domains = ! +local_domains
    senders = ! : ! *@lsearch;/etc/exim4/allowed.domains : ! lsearch;/etc/exim4/allowed.mails
    allow_fail
    data = :fail: You can not send mail from this mailbox from this server.

Если вы всё же выбрали «Split configuration into small files: no», то эти правила нужно вписать сразу после строчки

begin routers

в конфиге exim.

Это правило позволит отправлять только почту с любых ящиков доменов, которые перечислены в файле /etc/exim4/allowed.domains, или ящиков, перечисленных в файле /etc/exim4/allowed.mails. Обратите внимание на «или» — если домен написан в первом файле, то во втором смысле перечислять его ящики нет смысла. А если вам нужно разрешить отправку только с одного ящика домена — то не нужно писать его в allowed.domains, нужно написать конкретный ящик в allowed.mails

Ящики и домены нужно писать целиком (без регулярок, звездочек и прочего) по одному на строчку:

root@server:~# cat /etc/exim4/allowed.domains
domain1.tld
domain2.tld
root@server:~# cat /etc/exim4/allowed.mails
noreply@domain3.tld
robot@domain3.tld
webmaster@domain3.tld

Проверить можно так:

root@server:~# echo test | sendmail -f noreply@somepieceofshit.com youremail@domain.tld

где noreply@somepieceofshit.com — адрес отправителя, а youremail@domain.tld — адрес получателя.

В /var/log/exim4/mainlog в ответ на это вы увидите что-то в духе:

2013-12-08 17:10:47 1VphsU-0007JA-Vr ** youremail@domain.tld R=check_outgoing: You can not send mail from this mailbox from this server.

Как работают эти правила. Для начала стоит упомянуть, что у писем есть 2 важных заголовка — smtp.sender и header From:. smtp.sender, по сути, smtp-аккаунт, с которого происходит отправка письма. Поле From может содержать произвольную чушь, в том числе и совершенно левый ящик. Например, можно отправить письмо с smtp-аккаунта blah@someshit.com, но в поле From поставить admin@gmail.com. Чтобы посмотреть, кто реально отправил письмо — нужно будет лезть в его исходники. Большинство почтовых провайдеров при несовпадении этих полей кладёт такое письмо в спам. А спамеры, в свою очередь, шлют тонны писем с несовпадающими заголовками. Кхе.
Собственно, правило check_outgoing_from_header проверяет, чтобы в заголовке From содержалось имя smtp-аккаунта. Тут стоит упомянуть, что заголовок From: в нормальном письме выглядит примерно так — «inkvizitor68sl <root@vlad.pro>» (то есть помимо ящика содержит и имя/фамилию отправителя/ник или какую-нибудь другую чушь, которую он соизволит вписать туда). Именно поэтому, полагаться здесь на точное совпадение полей нельзя.

Второе правило (check_outgoing) проверяет, что значение поля sender (он же — smtp.sender, он же — smtp.name) подходит под одно из трех правил. Первое — sender пустой (сюда входят письма, сгенерированные самим почтовым сервером — например, отлупы о недоставке писем). Второе — sender совпадает с шаблоном *@любойдоменизспискаallowed.domains. Третье — поле sender целиком (пользователь@домен) указано в allowed.mails.

Так же учитывайте, что строчка в конфиге apache2 для вхоста вида:

php_admin_value sendmail_path "/usr/sbin/sendmail -t -i -f noreply@domain.tld"

выставляет значения поля sender в noreply@domain.tld (при том обойти это из самих скриптов уже не получится, только через htaccess). Поэтому, если ваша CMS шлет письма от ящика, отличного от указанного в этом месте — письма слать она перестанет.

Само собой, такая защита обходится (достаточно начать рассылать спам от «доверенных» ящиков) — авторизации всё ещё не требуется. Но мы можем в любой момент выключить ящик до того момента, пока не разберемся, что послужило источником спама на сервере.


Комментарии (14):

  1. Borz :

    Я так понимаю, аналогично можно и в обратном сделать через deny.domains и deny.mails

    Спасибо за статью)

  2. > Я так понимаю, аналогично можно и в обратном сделать через deny.domains и deny.mails
    Можно.

    Правило должно так выглядеть:
    senders = *@lsearch;/etc/exim4/denied.domains : lsearch;/etc/exim4/denied.mails

  3. Александр :

    Респект за статью! Уже давно бьюсь с проблемой исходящего спама, и наконец решил эту проблему

  4. klunnyy :

    Здравствуйте!
    Подскажите, пожалуйста, как настроить exim для следующего случая.
    На одном сервере — 3 домена, у домена1 почта на гугле, у домена2 и домена3 — на яндексе.
    Нужно, чтобы exim отправлял письма через smtp гугла и яндекса в зависимости от домена.
    Буду рад любой помощи.

  5. День добрый, хоть я и на FreeBSD, но, заметка ОЧЕНЬ помогла.
    Зарубил все попытки exima слать куда то наружу что то от локальных пользователей. Наверняка есть и более «изящный» метод, но, в написании конфига exim не силён. Подскажите — скажу спасибо :)
    Вот ещё один нерешённый вопрос остался:
    на одном IP есть 2 домена, и когда отправляется почта, то в HELO вставляется имя одного домена и тому и другому. Можно ли сделать подстановку в HELO имени того домена, с ящика которого собственно и отправляется почта?

  6. inkvizitor68sl :

    > на одном IP есть 2 домена, и когда отправляется почта, то в HELO вставляется имя одного домена и тому и другому. Можно ли сделать подстановку в HELO имени того домена, с ящика которого собственно и отправляется почта?

    Не стоит. В HELO должен быть тот hostname, который указан в ptr у машины (и прямая запись соответствующая должна существовать).
    Это нормальная практика, иначе бы не существовало возможности вменяемо делать сервисы вроде gmail или pdd.

  7. Немножко рано радовплся :)
    Есть в системе один ящик, с которого почта форвардится наружу, на mail.ru, так вот, форвард работать перестал :) В логах вижу: You can not send mail from here with From…

  8. > Есть в системе один ящик, с которого почта форвардится наружу, на mail.ru, так вот, форвард работать перестал :) В логах вижу: You can not send mail from here with From…

    Ну добавьте его в файл-то.

  9. Юрий :

    Да он там изначально в этом файлике. Уже и тот ящик на который форвард тоже добавил. Результат тот же.Как то это связано с тем, что ящик, на который форвардится почта не относится к локальным доменам.
    Форвард идёт через sieve dovecot

  10. Надо смотреть на полные заголовки писем, в общем. В угадайку играть я старый стал уже.
    Где то там в /var/spool/exim ищите письмо по id-нику из логов.

  11. В спулере не нашёл, к сожалению, а влогах exim:
    ** ящик на который форвард не доходит@bk.ru R=check_outgoing_from_header: You can not send mail from here with From: Имя владельца as sender: ящик с которого пришло письмо изначально@yandex.ru

  12. Можно ли как то сказать данной конструкции, что бы конкретный не локальный ящик никак не проверялся ни при каких пересылках?

  13. Ух бл, это нужно уметь condition к exim писать, а я не умею из головы =)

    Если без учета синтаксиса, то оно должно выглядеть как-то так:
    condition = ${if !match {$header_from:}{$sender_address}} and ${ ! {$sender_address} == «forwarder@domain.com»}
    Но написать это валидно я в упор не смогу (честно говоря — лень =)).

    Посмотри, как здесь пишутся составные условия — http://www.exim.org/exim-html-3.30/doc/html/filter_33.html.

    Тебе нужны 2 условия — ${if !match {$header_from:}{$sender_address}} и sender_address не равен forwarder@domain.com
    Тогда если sender_address совпадет с forwarder@domain.com, то check_outgoing_from_header применяться не будет.

Написать комментарий