Корпоративный MAIL RELAY-сервис::Журнал СА 3.2007
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
20.12.2019г.
Просмотров: 5400
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 6594
Комментарии: 0
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 7877
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 8169
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 7165
Комментарии: 0
Django 2 в примерах

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Корпоративный MAIL RELAY-сервис

Архив номеров / 2007 / Выпуск №3 (52) / Корпоративный MAIL RELAY-сервис

Рубрика: Безопасность /  Электронная почта

Яков Коваленко

Корпоративный MAIL RELAY-сервис

E-mail + UNIX + Exchange

Ваша почтовая система основана на MS Exchange, и вы опасаетесь подключать его напрямую в Интернет? Или вам требуется большая гибкость в маршрутизации и фильтрации почты, чего не может обеспечить упомянутый продукт? Так или иначе – UNIX-сервер и Sendmail с правильной настройкой избавят вас от головной боли и дадут мощные инструменты для работы с почтой.

В российских компаниях в большинстве случаев главным почтовым сервером является MS Exchange. Я не буду говорить ни плохих, ни хороших слов в его адрес, возьмем за данность, что он доминирует.

Как известно, продукты от MS часто подвергаются нападениям и становятся жертвами взломов, если они выставлены напрямую в Интернет. В связи с этим позиции UNIX-систем выглядят более чем привлекательно, и крупные компании стараются сделать front-end именно из UNIX-систем.

Как это реализуется?

Схема очень проста. Exchange-сервер устанавливается в изолированной от внешнего Интернета локальной сети. Обычно Exchange устанавливают на Высоконадежный Кластер, во избежание простоев из-за аппаратных сбоев. Поток почты поступает на Exchange через MX-серверы (Mail Exchangers, от названия DNS-записи, указывающей, какому серверу следует передавать почту), которые находятся под управлением UNIX-систем, не важно каких, Solaris, Linux, FreeBSD. Я советую ставить 3 MX-сервера для средней нагрузки в 20 000 – 50 000 сообщений в день.

Сломать UNIX-сервер в общем случае сложнее, чем Windows-сервер, при должном администрировании задача взломщика усложняется в разы.

В описываемом случае на UNIX-сервере работает MTA Sendmail, хотя общие принципы применимы к Postfix, Exim или другому MTA. Sendmail является на сегодняшний день одним из наиболее популярных агентов передачи сообщений в UNIX-подобных операционных системах. Кроме того, многие авторитетные люди признают его наиболее надежным и универсальным MTA из доступных в настоящее время.

MX-серверы устанавливаются либо в DMZ, либо одним сетевым интерфейсом подключены к Интернету, а другим – в приватную сеть. Так или иначе, у сервера должно быть соединение с Интернетом и с приватной сетью.

Конфигурирование Sendmail

В нашем конфигурационном файле sendmail.mc прописываем строку:

FEATURE(`mailertable',`hash -o /etc/mail/mailertable.db')dnl

В файле mailertable прописываем домен или домены, почту для которых мы принимаем, и адрес сервера, куда мы переправляем почту.

mydomain.ru smtp8:[XXX.XXX.XXX.XXX]

mydomain.com smtp8:[XXX.XXX.XXX.XXX]

На место XXX.XXX.XXX.XXX требуется поставить IP-адрес Exchange-сервера, тогда весь поток почты будет передаваться на него. В access-файле нужно прописать:

To:mydomain.ru RELAY

To:mydomain.com RELAY

XXX.XXX.XXX.XXX RELAY

На место XXX.XXX.XXX.XXX требуется поставить IP-адрес Exchange-сервера, тогда мы сможем принимать и пересылать почту от него. После всех этих манипуляций нужно пересобрать sendmail.cf, access и mailertable.

Это можно сделать либо отдельно для каждого файла, как инструкции к Sendmail, а можно создать в директории, где эти файлы расположены (в данном случае /etc/mail), Makefile с содержанием:

POSSIBLE += $(shell test -f bitdomain       && echo bitdomain.db)

POSSIBLE += $(shell test -f uudomain        && echo uudomain.db)

POSSIBLE += $(shell test -f genericstable   && echo genericstable.db)

POSSIBLE += $(shell test -f userdb          && echo userdb.db)

CFFILES   = sendmail.cf submit.cf

all: ${CFFILES} ${POSSIBLE} virtusertable.db access.db ?

    domaintable.db mailertable.db

userdb.db : userdb

        @makemap btree $@ < $<

%.db : %

        @makemap hash $@ < $<

%.cf : %.mc

        @if test -f /usr/share/sendmail-cf/m4/cf.m4; then \

                mv -f $@ $@.bak; \

                m4 $< > $@; \

        fi;

clean:

        rm -f *.db *~

(взято с RHEL, во FreeBSD есть аналогичный метод).

Тогда можно будет дать команду make и перезапустить Sendmail. Все, сервер принимает почту для наших доменов и пересылает ее Exchange-серверу. Указанные операции необходимо проделать на всех MX-серверах. На Exchange нужно указать UNIX-серверы в качестве смарт-хостов для пересылки почты. Как это конкретно делается – несложно найти в руководстве к Exchange.

Теперь много работы перекладывается на UNIX-серверы. Например, можно на этапе приема проверять почту в авторитетных DNSBL, фильтровать на вирусы и спам. Теперь, даже если Exchange-сервер выйдет из строя, почта будет накапливаться на MX-сервере и, как только проблема решится, он передаст ее по назначению. Ну и самое главное – наша почтовая система защищена и производительна, в Интернет смотрят только UNIX-серверы, и даже если один из них выйдет из строя – никто (надеюсь, кроме системного администратора) этого не заметит, так как рабочие MX-серверы примут на себя часть нагрузки от вышедшего из строя сервера.

Проблемы с мусорным трафиком

Сразу встает следующий вопрос – «Если почтовые ящики не хранятся локально на UNIX-сервере, то это значит, что Sendmail не знает, какой адрес существует, а какой нет, и будет принимать все письма с адресом, содержащим наш домен. Как быть с мусорным спам-трафиком, который отправляется зачастую на несуществующие адреса и вообще с бессмысленным набором символов?».

Действительно, спамеры так и делают – отправляют письмо на совершенно немыслимые адреса в нашем домене, например eokrjglskjg@mydomain.com. Или, например, некоторые сотрудники уволились, а почта им идет. Если бы отправляющий сервер соединялся непосредственно с Exchange-сервером, то Exchange сказал бы, что не знает такого адресата и почту не примет. А в нашем случае ему приходится отвечать на попытки отправить почту на несуществующие адреса уже нашим MX-серверам, которые приняли эту почту, потратили на нее трафик и, самое обидное, в большинстве случаев они не смогут отправить уведомление о недоставке, так как отправитель тоже, скорее всего, вымышленный. Доля таких писем составляет порядка 75%, что в масштабе больших предприятий выливается в очень солидные затраты на интернет-трафик.

Проведя многие часы в поисках, я так и не нашел готового удобоваримого способа решить эту проблему. Но выход есть! С помощью Perl можно написать Milter, – программу фильтрации почты на этапе SMTP-сессии. Сразу хочу сказать, что я не такой хороший программист на Perl, как этого хотелось бы, но написанный мной скрипт замечательно работает на загруженных серверах и экономит до 60-75% почтового трафика. Итак, схема работы сервиса.

Perl-скрипт принимает от Sendmail содержание RCPT TO, что является адресом получателя. Через модуль Net::Telnet генерирует telnet-сессию, через которую опрашивает Exchange-сервер на предмет наличия или отсутствия адреса. На основании полученного ответа или пропускает письмо, или отказывает в приеме. Для более производительной работы и для того чтобы по каждому письму не беспокоить Exchange-сервер, создается база существующих адресов, куда заносятся на сутки нормальные адреса и по их поводу Exchange уже не опрашивается. Просто и логично.

Для этого потребуется – сам Perl, модули Sendmail::Milter и Net::Telnet. Sendmail должен быть собран с libmilter. В последних версиях это делается по умолчанию. Устанавливать модули для Perl удобнее всего так:

perl -MCPAN -e shell

Если вы делаете это в первый раз, скрипт задаст кучу вопросов. Ответов по умолчанию, в общем случае, должно хватить, хотя возможны варианты. Когда появится приглашение cpan, введите:

install Net::Telnet

Аналогично с Sendmail::Milter.

Для установки Sendmail::Milter файл Makefile.pl требует наличия исходных кодов Sendmail. Если вы собирали из них, то проблем возникнуть не должно, но если у вас rpm или pkg, то потребуется несколько дополнительных телодвижений.

Установите devel-пакет Sendmail. Запустите скрипт:

#!/bin/sh

SRC_DIR=/usr/local/src/sendmail

mkdir -p ${SRC_DIR}/include

cp `rpm -ql sendmail-devel | grep '\.h$’` ${SRC_DIR}/include/

mkdir -p ${SRC_DIR}/libmilter

cp `rpm -ql sendmail-devel | grep '/lib’` ${SRC_DIR}/libmilter/

cd /root/.cpan/build/Sendmail-Milter-*

perl Makefile.PL ${SRC_DIR}/ ${SRC_DIR}/

Скрипт будет работать на RedHat-совместимых дистрибутивах Linux. Чтобы он заработал на других UNIX-подобных операционных системах, нужно найти аналог команды «rpm ‑ql» (вывести листинг файлов, относящихся к пакету). Например, в Solaris нужно с помощью утилит grep и awk отфильтровать файл /var/sadm/install/contents.

Далее в /root/.cpan/build/Sendmail-Milter-* дайте последовательно команды:

make

make test

make install

Должно заработать.

Итак, если установлены все необходимые модули, можно настраивать и запускать Perl-Milter.

Потребуется:

  1. Добавить в sendmail.mc описание Milter.
  2. Создать сам скрипт /usr/local/milters/mxcheck/mxcheck.pl.
  3. Создать init-скрипт /etc/init.d/mxcheck.
  4. Пересобрать sendmail.cf.
  5. Перезапустить sendmail.
  6. Запустить mxcheck.

В sendmail.mc добавляем:

INPUT_MAIL_FILTER(`mxcheck',`S=local:/var/run/mxcheck.socket')

define(`confINPUT_MAIL_FILTERS’,`mxcheck’)

Я не могу точно сказать, есть ли очередность мильтеров, но лучше поставить mxcheck до остальных мильтеров, если они у вас есть. Создаем скрипты и настраиваем их.

Листинг скрипта mxcheck.pl с комментариями:

#!/usr/bin/perl

#

# Любой продукт можно бесконечно оптимизировать и развивать.

# Скрипт должен послужить только отправной точкой.

# Здесь описаны лишь основные моменты, но они работают.

# Готовый продукт вы должны написать для себя сами.

#

#

# Определяем переменные и модули

#

  use Sendmail::Milter;

  use Net::Telnet ();

  use Socket;

  use AnyDBM_File;

  use strict;

  my  ( $uncheck,

        $scriptname,

        $sendmail_cf,

        $error_code,

        $reply,

        $exchangeip,

        $debug,

        $time,

        $basedir,

        %validsbase,

        $validsbase,

        $TTL,

        $rcpt_to,

        $line,

        $smtp,

        $msg,

        $IP,

        $enh_error_code

        );

#

# Директория, в которую кладется база

#

  $basedir = "/etc/mail";

#

# Название кэш-базы

#

  $validsbase = "validmails";

#

# Имя скрипта для поиска коннект-информации в sendmail.cf

#

  $scriptname = "mxcheck";

#

# Местоположение конфигурационного файла Sendmail

#

  $sendmail_cf = "$basedir/sendmail.cf";

#

# Адрес Exchange-сервера, у которого мы будем спрашивать существование адресов

#

  $exchangeip = "10.1.0.10";

#

# Адрес или подсеть, которая считается «своей», и почта от которой не проверяется

#

  $uncheck = "10.1";

#

# Режим отладки. Если поставить 1 – будет выдавать на STDOUT отладочные сообщения

#

 $debug = 0;

#

# Время жизни адреса в кэш-базе. Секунды

#

 $TTL = 86400;

 

#

# Определяем, какие SMTP-запросы нам нужно принять и обработать в скрипте. Здесь мы используем только

# адрес получателя и информацию о подключении, хотя можно использовать и другие. Почитайте мануал, очень интересно

  my %mx_callbacks =

  (

        'connect' =>    \&connect_callback,

        'envrcpt' =>    \&callback_rcptto,

  );

#

# Инициализируем Milter

#

  BEGIN:

  {

        @AnyDBM_File::ISA = qw(DB_File);

        my($conn) = Sendmail::Milter::auto_getconn("mxcheck", "$sendmail_cf");

    print "Found connection info for milter: $conn\n";

    if ($conn =~ /^local:(.+)$/) {

      my $unix_socket = $1;

        if (-e $unix_socket) {

          print "Attempting to unlink UNIX socket '$conn'\n";

            if (unlink($unix_socket) == 0) {

            print "failed\n";

            exit();

                 }

          print "successful\n";

                }

        }

    if (not Sendmail::Milter::auto_setconn("$scriptname", "$sendmail_cf")) {

    print "Failed to detect connection information\n";

    } elsif (not Sendmail::Milter::register("$scriptname",  \%mx_callbacks, SMFI_CURR_ACTS)) {

    print "Failed to register callbacks for milter\n";

    } else {

    print "Starting Sendmail::Milter$Sendmail::Milter::VERSION engine\n";

     if (Sendmail::Milter::main()) {

  print "Successful exit from the Sendmail::Milter \n";

        } else {

  print "Unsuccessful exit from the Sendmail::Milter \n";

                }

        }

  }

#

# Получение информации о подключении

#

  sub connect_callback

 {

        my $ctx = shift;

        my $hostname = shift;

        my $sockaddr_in = shift;

        my ($port, $iaddr);

        if ($debug == 1) {

                print "connect:\n";

                print "   + hostname: ‘$hostname’\n";

        }

#

# Если к нам подключился сервер из доверенной подсети – никаких проверок

#

   if (defined $sockaddr_in)

        {

        ($port, $iaddr) = sockaddr_in($sockaddr_in);

        $IP = inet_ntoa($iaddr);

        if ($IP =~ /^$uncheck/) {

        if ($debug == 1) { print "$IP in ALLOWED list.\n";

 }

                  return SMFIS_ACCEPT;

                }

        if ($debug == 1) {

           print "   port: '$port'\n";

           print "   iaddr: '" . inet_ntoa($iaddr) . "'\n";

                }

        }

        return SMFIS_CONTINUE;

  }

#

## Проверка адреса в Exchange

#

sub callback_rcptto  {

#

# Получаем информацию об адресате

#

        my $time=time();

        my $ctx = shift;

        my $rcpt_to = shift;

        $rcpt_to = lc($rcpt_to);

        $rcpt_to =~ s/\s*\<(.+?)\>\s*/$1/;

        my($href) = $ctx->getpriv();

        $ctx->setpriv($href);

#

# Посмотрим, есть ли этот адрес в кэш-базе

#

 unless (dbmopen(%validsbase,"$basedir/$validsbase",0600))

{

      return SMFIS_CONTINUE;

      if ($debug == 1) { print "Cannot open base \n"; }

      }

      if ($validsbase{$rcpt_to} ) {

      if ($debug == 1) { print "$rcpt_to FOUND in base \n"; }

#

# Похоже, что есть. Посмотрим TTL

#

    $valid = ($time - $validsbase{$rcpt_to});

        if ( $valid < $TTL) {

        if ($debug == 1) { print "$rcpt_to

    ALIVE TTL is ". ($valid - $TTL) ."

    time left - ". (($valid - $TTL) / 3600) ." hours \n"; }

#

# Есть и TTL не истек. Пропускаем

#

         return SMFIS_CONTINUE;

            } else {

           delete $validsbase{$rcpt_to};

#

# Есть, но TTL истек, удаляем из базы и продолжаем проверку

#

   if ($debug == 1) { print "$rcpt_to  DEAD. erasing \n"; }

              }

                }

        dbmclose(%validsbase);

#

# Если адрес не найден в кэш-базе или TTL истек – проверяем на Exchange

#

        if ($debug == 1) { print "Go to exch \n"; }

#

# Запускаем подпрограмму проверки на Exchange

#

        &check_exch($rcpt_to);

        $line =~ s/[\r\n]+$//;

#

# Возвращен ответ, начинающийся на 550, это означает, что нет такого адреса

#

     if ($line =~ /^550/) {

      $error_code = '554';

      $enh_error_code = '5.1.1';

      $reply = 'Unroutable address';

      $ctx->setreply($error_code, $enh_error_code, $reply);

#

# Пишем сообщение и отказываем в приеме

#

         return SMFIS_REJECT;

        }

#

# Возвращен ответ, начинающийся на 250, это означает, что такой адрес есть

#

        elsif ($line =~ /^250/) {

                $reply = $rcpt_to;

                $ctx->setreply('250', '2.1.5', $reply);

#

# Сохраняем его в кэш-базе на сутки

#

  unless (dbmopen(%validsbase,"$basedir/$validsbase",0600)) {

        return SMFIS_CONTINUE;

        if ($debug == 1) { print "Cannot open basefile \n"; }

                }

          $validsbase{$rcpt_to} = $time;

          dbmclose(%validsbase);

#

# Принимаем почту

#

          return SMFIS_CONTINUE;

        }

#

# Если ни то и ни другое – все равно принять, на всякий случай

#

        else {   return SMFIS_CONTINUE;  }

        }

#

# Проверка на Exchange

#

sub check_exch {

    my($rcpt_to) = @_;

    if ($debug == 1) { print "connecting to $exchangeip as $rcpt_to \n"; }

#

# Устанавливаем Telnet-соединение с Exchange-сервером

#

 $smtp = new Net::Telnet (Telnetmode => 0);

 $smtp->open(Host => $exchangeip, Port => 25, Errmode => "return");

        if ($debug == 1) { print "Processing Telnet... please wait - $smtp \n"; }

#

# Обрабатываем возможные ошибки

#

   $msg = $smtp->errmsg;

   if ($debug == 1) { print "Debug info - $msg \n"; }

#

# Если ошибка подключения – принимаем почту, на всякий случай

#

   if ($msg) {

     if ($msg =~ /^problem/) {

      $line = 250;

      if ($debug == 1) { print "connecting to $exchangeip falled \n"; }

        return $line;

        exit;

      } else {

        $line = 250;

      if ($debug == 1) { print "Unknown error with Telnet session \n"; }

          return $line;

          exit;

                }

        }

#

# Получаем первое SMTP-сообщение от сервера

#

        $line = $smtp->getline;

        if ($debug == 1) { print "$line \n"; }

#

# Отправляем HELO

#

        $smtp->print("HELO mail.mydomain.ru");

        $line = $smtp->getline;

        if ($debug == 1) { print "$line \n"; }

#

# Отправляем MAIL FROM:<>

#

        $smtp->print("MAIL FROM:<>");

        $line = $smtp->getline;

        if ($debug == 1) { print "$line \n"; }

#

# Отправляем RCPT TO с адресом получателя

#

        $smtp->print("RCPT TO:<$rcpt_to>");

        $line = $smtp->getline;

        if ($debug == 1) { print "$line \n"; }

#

# Отправляем QUIT

#

        $smtp->print("QUIT");

#

# Возвращаем результат – ответ сервера

#

        return $line;

}

В общем случае этого скрипта должно хватить. Но если вам необходимо делать много исключений – придется кое-что изменить. Но это уже тема программирования на Perl, которая выходит за рамки этой статьи.

С помощью Milter с почтой можно делать почти все, что угодно.

Листинг init-скрипта:

#!/bin/sh

# Source function library.

. /etc/rc.d/init.d/functions

RETVAL=0

milter_path="/usr/local/milters/mxcheck"

milter_name="mxcheck.pl"

start() {

    # Start daemons.

    echo -n "Starting $milter_name: "

    $milter_path/$milter_name &

    sleep 2

    RETVAL=$?

    echo

    return $RETVAL

}

stop() {

    # Stop daemons.

    echo -n "Shutdowning $milter_name: "

    killproc $milter_name

    RETVAL=$?

    echo

    PID=`ps axwu | grep $milter_name | grep perl | gawk '{print $2}'`

    if [ ! -z "${PID}" ]; then

        kill ${PID} 2>/dev/null

        sleep 1

    fi

    return $RETVAL

}

# See how we were called

case "$1" in

  start)

        start

        RETVAL=$?

        ;;

  stop)

        stop

        RETVAL=$?

        ;;

  restart)

        stop

        sleep 2

        start

        RETVAL=$?

        ;;

  status)

    status $milter_name

        RETVAL=$?

        ;;

  *)

       echo "Usage: $0 { start | stop | restart | status }"

        exit 1

esac

exit $RETVAL

Пересобираем sendmail.cf:

cd /etc/mail

make

Перезапускаем сервисы:

/etc/init.d/sendmail restart

/etc/init.d/mxcheck start

Если все настроено правильно и все запустилось – поздравляю, у вас правильная система, которая еще и трафик экономит.

Благодарности

Огромное спасибо Виктору Устюгову aka Сorvax за советы и помощь в борьбе со спамом на relay-серверах. Большое спасибо моим коллегам по работе Григорьеву Игорю Григорьевичу и Королеву Игорю Александровичу за помощь в Perl и UNIX.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru