Полезные скрипты/настройки для exim.

EXIM, sendmail, postfix, Dovecot и прочие. Решение проблем связанных с работой электронной почты

Модератор: xM

Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
Аватара пользователя
Laa
ст. лейтенант
Сообщения: 1032
Зарегистрирован: 2008-02-21 18:25:33
Откуда: Украина, Россия

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение Laa » 2009-02-23 10:29:33

Переменная rcpt_fail_count получает зеачение после того, как ввели второй и последующий RCPT TO с несуществующим получателем. То есть на первом эта переменная равна нулю (блин, или по дефолту она равна нулю?), на втором -- единице, на третьем -- двум и так далее.

Это важно знать и использовать.
Например, чтобы на втором неправильном получателе выдавать отлуп нужно делать так:

Код: Выделить всё

        drop    condition       = ${if >={$rcpt_fail_count}{1} {1}{0}}
                log_message     = Too many rcpt_fail_count (${eval:$rcpt_fail_count+1})
                message         = Reject. Too many unknown recipients.
exim: помните, что выдавая deny, вы можете недоставить ваше же письмо, зарубив sender-verify удаленного MTA к вашему MTA!!!

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/

dnk
проходил мимо
Сообщения: 4
Зарегистрирован: 2009-01-14 18:10:46

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение dnk » 2009-03-18 16:12:19

Сбор статистики (если нужно)

В БД создаем таблицу

Код: Выделить всё

create table `log_user` (`from` char(50),`to` char(50),`data` datetime,`size` int(8));
В конфиге exim вставляем макрос

Код: Выделить всё

MYSQL_STAT = INSERT INTO log_user VALUES ('$sender_address','$acl_m10@$acl_m11',now(),$message_size)
Ну и в секцию acl конфига exim вставляем

Код: Выделить всё

# Записываем данные : кто-кому-когда и сколько :)
  warn
        set acl_m10    = $local_part
        set acl_m11    = $domain
         set acl_m19   = ${lookup mysql{MYSQL_STAT}}

Аватара пользователя
abanamat
сержант
Сообщения: 255
Зарегистрирован: 2007-03-15 11:24:26
Откуда: Питер
Контактная информация:

greplog.sh

Непрочитанное сообщение abanamat » 2009-04-14 13:57:12

Для чего это. Для того чтобы найти что-то в логе exim.
Как работать. Скрипту кормится что-то, что он grep - ом достает из лога exim. Дальше два этапа. 1 - выбор лога из имеющихся, 2 - выбор чего показывать из найденного (доставки/отлупы/все).

Описание.

Скрипт представляет из себя портянку на awk в оболочке из sh. Почему не sh целиком - потому что в разных ОС, в разных локалях и в разных логин шелах, результат будет разный. Awk позволяет уйти от этих проблем. Почему sh оставлен - потому что надо два раза получить пользовательский ввод с клавиатуры плюс отмониторить Ctrl+C (и потереть временный файл, создающийся в процессе работы). Сначала в логе ищутся совпадения со строкой запроса, потом утилиты считывают лог второй раз и выводят результат. Основная нагрузка в скрипте ложится на grep (во время второго прохода) который, в случае если найдено много доставок, получает немалый список регулярных выражений (идентификаторов очередей), и каждая строка лога проверяется на предмет совпадения ее с идентификатором очереди из списка. Вывод реджектов реализован на awk, временный файл в этом случае не используется (после первого прохода grep -ом в памяти образуется массив с номерами нужных строк файла лога). Каждый файл лога, получающийся в результате ротации скриптом exicyclog (входит в состав дистрибутива exim), считается логом суток. Иначе говоря, если в одном логе поместились данные за три дня - скрипт будет показывать дату первого из этих трех дней и результат будет враньем в разрезе даты. Почему так сделано - потому что скрипт делался под задачу. Задача стояла - быстро найти в многомегабайтном логе по любому отрезку слова услышаному по телефону от клиента, нужную строчку (или строчки) лога. А ротация при таких размерах лога получается как раз сутки - один файл. Попросту говоря, работа с маленькими логами не рассматривалась. Также не учитывалась возможность использования НЕ- exim транспортов локальной доставки (а некоторые для этого используют dovecot). Настройки логгирования для конфига exim приведены в конце скрипта.

UPD релиз №1 (немного унификации для работоспособности и в линухе тоже). Актуальная версия скрипта всегда по адресу http://www.abanamat.info/src/Mail/greplog.sh

Код: Выделить всё

#!/bin/sh

to_grep="$*"

if [ ${#to_grep} = 0 ]
then
    echo "
    STOP: Where is search string?
" > /dev/stderr
    exit 1
fi

tmp_major_filename="/tmp/searchmajortmp$$"
rm -f "${tmp_major_filename}"

# Мониторим Ctrl+C чтобы не оставлять хвостов из временных файлов.
trap 'rm -f ${tmp_major_filename} ;echo ; exit 13' TERM INT

exlog_log="/var/log/exim/exim_main.log"

if [ ! -r "${exlog_log}" ]
then
    echo "
	STOP: Where is exim log?
" > /dev/stderr
    exit 1
fi

today_is=`date +%Y-%m-%d`

echo "1" | awk -v today_is="${today_is}" -v rex_log="${exlog_log}" '{
    dl_cn = 0					# устанавливаем счетчик дней в 0
    # Получаем список файлов лога exim-a, считаем их количество.
    cmd_get_file_list=("ls "rex_log"*")
    while ((cmd_get_file_list |getline)> 0) {
	++fl_cn
    }
    close (cmd_get_file_list)

    # Узнаем что у нас за ОС
    cmd_what_os=("uname -s")
    cmd_what_os |getline os_is
    close(cmd_what_os)

    while (dl_cn < fl_cn) {
	# Получаем три значения в массиве (год, месяц, день) - текущая дата.
	if (dl_cn == 0) {
	    split(today_is,TEMPTODAY,"-")
	    today_is_month = TEMPTODAY[2]
	    month_arg = TEMPTODAY[2]
	} else {
	    cmd_month_arg = (today_is_month - month_arg)
	    # Получаем три значения в массиве (год, месяц, день) - последнее число нужного нам предыдущего месяца.
	    if (os_is == "FreeBSD") {
		cmd_date_last_month=("date -v-"cmd_day_arg"d -v-"cmd_month_arg"m +%Y-%m-%d")
	    } else {
		cmd_date_last_month=("date -d \""cmd_day_arg" days ago "cmd_month_arg" months ago\" +%Y-%m-%d")
	    }
	    cmd_date_last_month |getline
	    split($0,TEMPTODAY,"-")
	    close (cmd_date_last_month)
	}

	# Выясняем до какого числа месяца будем делать декремент.
	days_until = (fl_cn - dl_cn)
	if (days_until > 0) {
	    day_until = 1
	} else {
	    day_until = days_until
	}

	cmd_day_arg = TEMPTODAY[3]

	# Получаем список дат в нисходящем порядке, забиваем в массив.
	for (tmp_td=TEMPTODAY[3];tmp_td>=day_until;tmp_td--) {
	    # Заодно пририсовываем нули для красоты.
	    if (length(tmp_td) == 1) {
		day_number = ("0"tmp_td)
	    } else {
		day_number = tmp_td
	    }
	    FILESLIST[++dl_cn] = (TEMPTODAY[1]"-"TEMPTODAY[2]"-"day_number)
	}

	# Проверяем первая ли у нас итерация цикла.
	if (month_arg != today_is_month) {
	    month_arg--
	}
	split("",TEMPTODAY)
    }

    # Печать массива.
    printf "\n"
    for (xs=1;xs<=fl_cn;xs++) {
	printf "\t%s%s%s\t%s\n" , "(", xs, ")", FILESLIST[xs]
    }
    split("",FILESLIST)
	printf "\n\t"
    exit
}'

read -p "Enter Digit:	" num_to_grep

if [ ${#num_to_grep} = 0 ]
then
    echo "
	STOP: Where is digit?
" > /dev/stderr
    exit 1
fi


awk -v tmfname="${tmp_major_filename}" -v today_is="${today_is}" -v num_to_grep="${num_to_grep}" -v rex_log="${exlog_log}" -v to_grep="${to_grep}" '{
    # Проверяем пользовательский ввод. Устанавливаем переменную w_show.
    if ($0 ~ /^[1-3]$/) {
	w_show=$0
	exit
    } else {
	printf "\n\t%s\n\n", msg_wrong_number > err_log
	split("",QUEUEARR)
	split("",REJARRAY)
	was_error = 1
	exit 1
    }
} BEGIN {
    q_cn = 0
    r_cn = 0
    err_log = "/dev/stderr"

    # Сообщения
    msg_nothing_to_show = "STOP: Nothing to show."
    msg_no_records = "STOP: No Records."
    msg_wrong_number = "STOP: Wrong number."
    msg_header_major = "SUCCESS"
    msg_header_minor = "FAILURE"
    msg_header_summ = "SUMMARY"

    # Паттерн очереди
    patt_QUEbe = "^[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]-[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]-[A-Za-z0-9][A-Za-z0-9]$"

    # Получаем список файлов лога exim-a, добавляем их в массив, считаем их количество.
    cmd_get_file_list=("ls "rex_log"*")
    while ((cmd_get_file_list |getline)> 0) {
	FILESLIST[++fl_cn] = $0
    }
    close (cmd_get_file_list)

    # Проверяем пользовательский ввод. Получаем имя файла, проверяем заархивирован ли он, устанавливаем необходимые переменные.
    if (num_to_grep ~ /^[0-9]+$/ && (num_to_grep <= fl_cn && num_to_grep != 0)) {
	ex_log = FILESLIST[num_to_grep]
	tmpfilename_parts = split(FILESLIST[num_to_grep],TMPFILENAME,".")
	if (TMPFILENAME[tmpfilename_parts] == "gz") {
	    first_grep = ("zgrep -ni \""to_grep"\" "ex_log)
	    second_grep = ("zgrep -f "tmfname" "ex_log)
	    pr_ex_log = ("zcat "ex_log)
	} else {
	    first_grep = ("grep -ni \""to_grep"\" "ex_log)
	    second_grep = ("grep -f "tmfname" "ex_log)
	    pr_ex_log = ("cat "ex_log)
	}
	split("",TMPFILENAME)
	split("",FILESLIST)
    } else {
	printf "\n\t%s\n\n", msg_wrong_number > err_log
	was_error = 1
	exit 1
    }

    # Запускаем греп лога екзима. Если в строке в нужном месте есть совпадение с паттерном очереди, и это не реджект,
    # добавляем очередь в массив очередей. Иначе номер строки лога в массив номеров строк лога.
    while ((first_grep |getline)> 0) {
	if (split($0,TMPLINE,":")>0) {
	    testqueue = substr(TMPLINE[4],4,16)
	    if (testqueue ~ patt_QUEbe) {
		if (substr(TMPLINE[4],21,8) == "rejected") {
		    REJARRAY[++r_cn] = TMPLINE[1]
		} else if ((testqueue in QUEUEARR) == 0) {
		    QUEUEARR[testqueue]
		    ++q_cn
		}
	    } else {
		REJARRAY[++r_cn] = TMPLINE[1]
	    }

	}
	split("",TMPLINE)
    }
    printf "\n"
    close (first_grep)

	# Считаем и печатаем сколько мы всего нашли. Заодно приглашение ввода номера того что хочется увидеть если чего-то нашли.
	printf "\t%s\t%s\t%s\n", "(1)", msg_header_major, q_cn
	printf "\t%s\t%s\t%s\n", "(2)", msg_header_minor, r_cn
	printf "\t%s\t%s\t%s\n", "(3)", msg_header_summ, (q_cn+r_cn)
	if ((q_cn+r_cn) == 0) {
	    printf "\n\t%s\n\n", msg_nothing_to_show > err_log
	    split("",QUEUEARR)
	    split("",REJARRAY)
	    was_error = 1
	    exit 1
	} else {
	    printf "\n\t%s\t" , "Enter Digit:"
	}
} END {
    if (was_error == 1) {
	exit 1
    }

    # Получаем значение год-месяц-день из лога который будем показывать.
    pr_ex_log | getline
    close (pr_ex_log)
    actdte = substr($0,1,10)

    # Устанавливаем переменные - элементы декора.
    header_major = sprintf("%s%s%s%s%s", "-------------------------------- ",msg_header_major," ---------------------------------------- ",actdte," ----------------------")
    header_minor = sprintf("%s%s%s%s%s", "-------------------------------- ",msg_header_minor," ---------------------------------------- ",actdte," ----------------------")
    footer = sprintf("%s%s%s\n", "------------------------------------------------------------------------------- ",actdte," ----------------------")
    splitter_completed = sprintf("%s", "+ ------------ c -- o -- m -- p -- l -- e -- t -- e -- d ---------------------- +")

    cmd_more = "more"

    # Пользователь возжелал увидеть очереди.
    if (w_show == 1) {
	if (q_cn == 0) {
	    split("",QUEUEARR)
	    split("",REJARRAY)
	    printf "\n\t%s\n\n", msg_no_records > err_log
	    exit 1
	} else {
	    y = 1
	    z = 0
	    # Сбрасываем содержимое массива очередей во временный файл
	    for (tq in QUEUEARR) {
	        print (tq) >> tmfname
	    }
	    print(header_major)
	    # Грепаем лог екзима используя временный файл в качестве файла паттернов.
	    while ((second_grep |getline)> 0) {
	        if (length($0) == 46) {
	    	    if (substr($0,38,9) == "Completed") {
		        print (splitter_completed) | cmd_more
		    } else {
		        print(substr($0,12)) | cmd_more
		    }
		} else {
		    print(substr($0,12)) | cmd_more
		}
	    }
	    close (second_grep)
	    close (cmd_more)
	    print(footer)
	}
    # Пользователь возжелал увидеть отлупы.
    } else if (w_show == 2) {
	if (r_cn == 0) {
	    split("",QUEUEARR)
	    split("",REJARRAY)
	    printf "\n\t%s\n\n", msg_no_records > err_log
	    exit 1
	} else {
	    y = 1
	    z = 0
	    print(header_minor)
	    while ((pr_ex_log |getline)> 0) {
		z++
		if(z == REJARRAY[y]){
		    print(substr($0,12)) | cmd_more
		    y++
		}
	    }
	    close (pr_ex_log)
	    close (cmd_more)
	    print(footer)
	}
    # Пользователь возжелал увидеть все.
    } else if (w_show == 3) {
	if (r_cn != 0) {
	    y = 1
	    z = 0
	    print(header_minor)
	    while ((pr_ex_log |getline)> 0) {
		z++
		if(z == REJARRAY[y]){
		    print(substr($0,12)) | cmd_more
		    y++
		}
	    }
	    close (pr_ex_log)
	    close (cmd_more)
	    print(footer)
	}
	if (q_cn != 0) {
	    y = 1
	    z = 0
	    # Сбрасываем содержимое массива очередей во временный файл
	    for (tq in QUEUEARR) {
	        print (tq) >> tmfname
	    }
	    print(header_major)
	    # Грепаем лог екзима используя временный файл в качестве файла паттернов.
	    while ((second_grep |getline)> 0) {
	        if (length($0) == 46) {
	    	    if (substr($0,38,9) == "Completed") {
		        print (splitter_completed) | cmd_more
		    } else {
		        print(substr($0,12)) | cmd_more
		    }
		} else {
		    print(substr($0,12)) | cmd_more
		}
	    }
	    close (second_grep)
	    close (cmd_more)
	    print(footer)
	}
    }
    split("",QUEUEARR)
    split("",REJARRAY)
}' -

rm -f "${tmp_major_filename}"

# Настройки логгирования в конфиге exim:
# --
# log_file_path = /var/log/exim/exim_%s.log
# write_rejectlog = no
#
# log_selector = \
#    +all_parents \
#    +connection_reject \
#    -host_lookup_failed \
#    -incoming_interface \
#    -lost_incoming_connection \
#    +received_sender \
#    +received_recipients \
#    +smtp_confirmation \
#    +smtp_syntax_error \
#    +smtp_protocol_error \
#    -queue_run
#
# syslog_timestamp = yes
#
# --
#
# Ver 1.0
#
Последний раз редактировалось abanamat 2009-09-25 9:59:54, всего редактировалось 5 раз.

bubnov-pi
проходил мимо

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение bubnov-pi » 2009-05-20 17:29:29

SPAMTRAP + Autoblacklist:
Родилась идея несколько развить тему автоблеклистинга и ящиков-ловушек (начало мысли было положено постом dikens3 - 2007-06-06 13:47:35).
Описание идеи:
На сервере есть ряд адресатов, реально не используемых, но обожаемых спамерами (director@, office@, info@ etc.) ну и можно ещё адресочек какой-нибудь замаскировать на сайте.
Почтарь настроен практически по статье http://www.lissyara.su/?id=1728 (реально менялось многое, но к нашему вопросу это не относится).
Идея - зарезать письмо целиком, если хотя бы один из адресатов попадает в список spamtrap и блокировать данный хост на приём от него почты на непродолжительное время (1 час, например - а так - на своё усмотрение).
Для этого я решил создать две дополнительные таблицы - для списка "ловушек":

Код: Выделить всё

-- База данных: `exim`
-- Структура таблицы `traplist`

CREATE TABLE IF NOT EXISTS `traplist` (
  `trapmail` varchar(25) CHARACTER SET cp1251 NOT NULL,
  `active` tinyint(1) unsigned NOT NULL DEFAULT '0',
  UNIQUE KEY `trapmail` (`trapmail`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 COLLATE=cp1251_bin;
и для хранения заблокированных хостов:

Код: Выделить всё

-- База данных: `exim`
-- Структура таблицы `traphost`

CREATE TABLE IF NOT EXISTS `traphost` (
  `host_IP` varchar(15) COLLATE cp1251_bin NOT NULL,
  `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`host_IP`)
) ENGINE=MyISAM  DEFAULT CHARSET=cp1251 COLLATE=cp1251_bin;
Далее для успешной блокировки многоадресных сообщений с адресатом-ловушкой на стадии acl_smtp_predata, надо в разделе описания ACL добавить строчку:

Код: Выделить всё

acl_smtp_predata = acl_check_predata
В разделе описания ACL acl_smtp_rcpt (acl_check_rcpt) вводим отказ в получении писем от хостов, занесённых в локальный чёрный список с одновременным удалением просроченных записей (очистку я умышленно ввёл в сообщение с отказом - для визуального контроля через Exilog):

Код: Выделить всё

  deny    condition     = ${if eq{$sender_host_address} \
                                    {${lookup mysql{SELECT `host_IP` \
                                       FROM `traphost` WHERE \
                                       `host_IP`='$sender_host_address'}}} \
                               {yes}{no}}
          message       = You are blacklisted by local trap. Sorry. (${lookup mysql{DELETE FROM `traphost` \
                        WHERE `Timestamp` < TIMESTAMPADD(HOUR,-1,CURRENT_TIMESTAMP)}})
...была ещё идея поместить тут же инструкцию на обновление поля `Timestamp` для данного хоста, но решил от неё отказаться, вспомнив, как сам столкнулся с ситуацией, когда пытался пробиться сквозь похожую защиту и решил не зверствовать :"":
И в том же разделе, где-то в районе настройки паузы для незнакомых хостов-отправителей, разместить такой блок:

Код: Выделить всё

  # "Помечаем" письмо, если хотя бы один из получателей - в спамтрапе
  #  - для дальнейшего отказа в получении на стадии ...predata
  # (там недоступны данные о получателях)
  warn
        condition     = ${if eq{$local_part@$domain}{${lookup mysql{SELECT `trapmail` \
                            FROM `traplist` WHERE \
                            `active`='1' AND \
                            `trapmail`='$local_part@$domain'}}}{yes}{no}}
        set acl_m18 = 2
        # эта строка введена для предотвращения попыток многократного занесения в "чёрный
        # список" хостов, рассылающих спам в несколько потоков
        set acl_m0 = 0s
        # тут пишем в лог информацию о сработавшем адресе-ловушке, кому надо, а затем
        # помещаем адрес хоста в БД - мне, при отладке, такая конструкция больше всего понравилась
        logwrite = Trap: ${lookup mysql{SELECT `trapmail` \
                            FROM `traplist` WHERE \
                            `active`='1' AND \
                            `trapmail`='$local_part@$domain'}} - ${lookup mysql{INSERT INTO `traphost` \
                            (`host_IP`) \
                            VALUES \
                            ('$sender_host_address')}}
И, собственно, правила, блокирующие "попавшие в ловушку" письма (на этапе acl_smtp_predata):

Код: Выделить всё

# Тут идут ACL, срабатывающие СРАЗУ после получения команды DATA, но ДО данных
acl_check_predata:
  # Режем всё письмо, если хотя бы один из получателей - в спамтрапе
  # warn logwrite = ACL predata Trap activated.
  deny message = "Temporary error, unknown user(s)."
       condition = ${if eq{$acl_m18}{2}{yes}{no}}
    #   logwrite  = Trap activated 

  accept
В приведённом примере, хост, единожды "попавший в ловушку", уже не дойдёт до второго попадания - сработает "чёрный список".
Объективности ради, надо заметить, что данное правило отсеивает не так уж много писем - с применением SPF и фильтров по RDNS-имени хоста-отправителя и имени, передаваемого в HELO, характерных для сетей общественного доступа (pool, dialup и др.) на нём "зарезается" порядка 10% от общего числа спама.
Ещё один важный момент - данными правилами не должны блокироваться письма, отправляемые на postmaster@ (RFC 2821, пункт 4.5.1) - они и не блокируются, если прописан убранный "в оригинале настроек" блок:

Код: Выделить всё

  accept  local_parts   = postmaster
          domains       = +local_domains
Но это на любителя - у кого закомментирован - будет резаться и почта к постмастеру.

Надеюсь на конструктивную критику со стороны более опытных коллег, т.к. себя считаю далёким от звания профессионала.

eem-kz
проходил мимо

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение eem-kz » 2009-06-12 7:49:30

EXIM отличается от sendmail'а более понятным конфигом и который можно настроить практически на все случаи.
Расскажу, как сделать поддержку технологии greylist (серый список) в exim для обеспечения блокирования порядка 90% спама.

Технология greylist заключается в том, что при первой попытке отправить нам письмо, передающей стороне передается код

Код: Выделить всё

451 Temporary reject
Это заставляет нормальный передающий узел повторить попытку передачи письма позднее (интервал повторов зависит от его настроек)
Через определенное время письмо перепосылается и мы его принимаем.
Таким образом, нормальный узел передает письмо с какой-то задержкой.
Спам рассылки, как правило, делаются либо через открытые релеи, либо через всякие прокси, либо напрямую. Если спамщики будут отрабатывать 4xx ошибку и делать переповторы отправки спама, то это резко уменьшит скорость рассылки (да и обработка факта отправки усложнится).
Поэтому, спамщики будут пытаться отправить спам через другие сервера, наивно предполагая, что заблокирован конкретный узел.
Разумеется, получат такой-же облом

Нужно:
настроенный exim версии 4.x с поддержкой mysql (я вообще всю инфу храню в базе, в т.ч. настройки)
Как настроить exim для работы с mysql рассказывать не буду - это тривиально.
Необходимо создать в базе табличку, для хранения текущей информации greylist:

Код: Выделить всё

CREATE TABLE `Greylist` (
  `id` bigint(20) NOT NULL auto_increment,
  `relay_ip` char(15) default NULL,
  `sender` char(64) default NULL,
  `recipient` char(64) default NULL,
  `block_expires` int(11) NOT NULL default '0',
  `record_expires` int(11) NOT NULL default '0',
  `create_time` int(10) unsigned NOT NULL default '0',
  `pass_count` int(10) unsigned NOT NULL default '0',
  `block_count` int(10) unsigned NOT NULL default '0',
  `ehlo` char(64) default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `relay_ip` (`relay_ip`,`sender`,`recipient`)
);
Теперь надо прописать в конфиг exim'а:
а) в самом начале прописать макросы:

Код: Выделить всё

GREYLIST_TEST=SELECT concat("id=",id," ts=",(unix_timestamp()-block_expires)) \
        from Greylist \
        where relay_ip='${quote_mysql:$sender_host_address}' and \
        sender='${quote_mysql:$sender_address}' and \
        recipient='${quote_mysql:$local_part}@${quote_mysql:$domain}' limit 1
GREYLIST_ADD=INSERT into Greylist (relay_ip,sender,recipient,\
        block_expires,record_expires,create_time,block_count,ehlo) \
        values \
        ('${quote_mysql:$sender_host_address}',\
        '${quote_mysql:$sender_address}',\
        '${quote_mysql:$local_part}@${quote_mysql:$domain}',\
        unix_timestamp()+60*15,unix_timestamp()+60*60*24,unix_timestamp(),1,\
        '${quote_mysql:$sender_helo_name}')
GREYLIST_UPD1=UPDATE Greylist set pass_count=pass_count+1,\
        record_expires=unix_timestamp()+60*60*24 where id=$acl_m2
GREYLIST_UPD2=UPDATE Greylist set block_count=block_count+1 where id=$acl_m2
в блоке ACL настроек после RCPT TO в нужном месте прописать сами директивы:

Код: Выделить всё

# set variables
warn set acl_m1 = ${lookup mysql{GREYLIST_TEST}{$value}{id=-1 ts=0}}
     set acl_m2 = ${extract{id}{$acl_m1}{$value}{unknown}}
     set acl_m3 = ${extract{ts}{$acl_m1}{$value}{unknown}}

# defer if first attempt
defer   hosts = ! +relay_hosts
        condition = ${if < {$acl_m2}{0}{yes}{no}}
        set acl_m4 = ${lookup mysql{GREYLIST_ADD}{$value}{0}}

# defer if time < block_time
defer   hosts = ! +relay_hosts
        condition = ${if > {$acl_m2}{0}{yes}{no}}
        condition = ${if < {$acl_m3}{0}{yes}{no}}
        set acl_m4 = ${lookup mysql{GREYLIST_UPD2}{$value}{0}}

# pass defers if time > block_time and update count
warn    hosts = ! +relay_hosts
        set acl_m4 = ${lookup mysql{GREYLIST_UPD1}{$value}{0}}

Описание:
а) при первой попытке отправить нам почту (когда тройка sender_host_address, sender_address, recipient_address отсутствует в таблице), письмо отвергается; факт попытки заносится в табличку
б) при попытке спустя время, меньшее 60*15 секунд, письмо отвергается; наращивается счетчик отвергнутых попыток
в) --//-- время большее 60*15 секунд - письмо принимается; апдейтится время устаревания записи и счетчик принятых попыток

Так-же, в cron полезно прописать следующее:

Код: Выделить всё

mysql база -e 'delete from Greylist where unix_timestamp()>record_expires;'
чтобы удалять устаревшие записи, т.е. не было повторных писем спустя 60*60*24 секунд от последней попытки.

PS. информация дается AS IS. у меня всё работает.
PPS. разумеется, в конфиге должны быть и другие правила; поэтому важно "всунуть" эти настройки в нужное место. а так-же, ввести дополнительные проверки, например, у меня есть еще настройка срабатывания этих правил по конкретным адресам получателей. т.е. greylist работает не для всех.
PPPS. технология не моя. я только сделал всё средствами exim'а. до этого greylist работал через внешний скрипт.

Laa: Чтобы не раздувать тему, вставлю следующее сообщение этого же пользователя:
Я нашол ссылку http://lug.ivanovo.ru/f/topic_show.pl?tid=259
оставте сюда свое мнении
Последний раз редактировалось Laa 2009-06-24 15:18:52, всего редактировалось 3 раза.
Причина: Почистил тему от лишних постов.

W16
проходил мимо
Сообщения: 9
Зарегистрирован: 2007-12-11 13:08:02

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение W16 » 2009-11-03 17:14:16

Технология greylist заключается в том, что при первой попытке отправить нам письмо, передающей стороне передается код

Код: Выделить всё

451 Temporary reject
Это заставляет нормальный передающий узел повторить попытку передачи письма позднее (интервал повторов зависит от его настроек)
Через определенное время письмо перепосылается и мы его принимаем.
Таким образом, нормальный узел передает письмо с какой-то задержкой.
Спам рассылки, как правило, делаются либо через открытые релеи, либо через всякие прокси, либо напрямую.
Практика показала, что mail.ru, например, не всегда отправляет повторное письмо с того же IP, что и изначально.
Соответственно, письма с mail.ru до получателей не доходят.
Аналогично с gmail.

Аватара пользователя
dikens3
подполковник
Сообщения: 4856
Зарегистрирован: 2006-09-06 16:24:08
Откуда: Нижний Новгород
Контактная информация:

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение dikens3 » 2009-11-03 21:45:32

Смысл и назначение почтового сервера.
Почтовый сервер, как правило (далее пункт 1), ОБСЛУЖИВАЕТ СВОЙ ДОМЕН(Ы), для которого он обязан принимать почту без аутентификации.
Почтовый сервер предназначен для отправки почтовых сообщений:
1. Сразу в папку пользователю - передаёт другому процессу. (dovecot и т.п.)
2. Другому серверу (relay)

Следовательно из вышеописанного, правило exim выглядит так:

Код: Выделить всё

accept - Т.е. принимать всё и от всех.
Далее начинаем понимать, что надо определяться с некоторыми вещами (пункты 1 и 2). Если Relay нам не нужен, тогда добавляем соответствующее условие и получаем:

Код: Выделить всё

# Принимаем письма для нашего домена
  accept    domains       = +local_domains
            endpass
            message       = "Unknown user"
            verify        = recipient

  deny      message - Это типа не релей
Далее начинам осознавать всю проблему. Спамеры начинают слать спам используя всевозможные механизмы (подделку адресов, хостов, фальшивые почтовые сервера, баги, вирусы, продажных или непутевых админов и т.д.)
Админы начинают придумывать защиту от спама для улучшения качества сервиса SMTP. Появляются рекомендации - типа настроек DNS.
Также появляется множество способов защиты от спама, но в общем случае это всего-лишь дополнение в правила описанные выше:

Код: Выделить всё

Своих пользователей лучше определить и исключить из проверок на спам. Т.е. ненужно их блокировать.
Всякие проверки на спам с блокировкой или нет, по желанию.

# Принимаем письма для нашего домена
  accept    domains       = +local_domains
            endpass
            message       = "Unknown user"
            verify        = recipient

  deny      message - Это типа не релей
P.S. Просто для понимания и без умных мыслей.
Лучше установить FreeBSD, чем потратить 30 лет на Linux'ы и выяснить какой из них хуже.

Аватара пользователя
mastertron
мл. сержант
Сообщения: 107
Зарегистрирован: 2009-02-06 20:48:53
Откуда: Украина, Одесса

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение mastertron » 2010-01-07 22:37:10

В принципе так делать нельзя (не по правилам), но меня устроило очень.
Если нет адреса получателя ни в "Кому" ни в "Копия".
Запоминаем адрес получателя, потому как в секции acl_check_data: его уже нет.

Код: Выделить всё

acl_check_rcpt: 
....
     warn   set acl_m0	=$local_part@$domain
Контролируем наличие последнего в поле "Кому:" или "Копия:".

Код: Выделить всё

acl_check_data:
...
     deny     message   = access denied
  	    condition   = ${if match { ${lc:$rheader_To:} or ${lc:$rheader_CC:}}{$acl_m0}{no}{yes}}
Я набрасываю балл и в черные списки.
Можно следом еще прилепить контроль количества адресатов с тойже целью.
Например

Код: Выделить всё

condition   = ${if match {${lc:$rheader_To:}}{.*,.*,.*,}{yes}{no}} 
Ну ... типа. И тоже на "СС:". Эт чтоб "больше двух не собираться".
Работает почемуто только в первой строке, а чтоб не "многострочили" - следом

Код: Выделить всё

 condition   = ${if match {${lc:$rheader_To:}}{.*\n\.*\n}{yes}{no}} 
Если адресов не впихивается в одну строчку - убить. Можно продлить правило на две, три строчки.
Против "сам себе" тоже просто:

Код: Выделить всё

  message = From me? You joke!
  condition = ${if match {${lc:$rheader_From:}}{${acl_m0}}{yes}{no}}
Ну и естестно везде предшествует

Код: Выделить всё

hosts = !+relay_from_hosts
Последний раз редактировалось mastertron 2010-03-30 0:11:53, всего редактировалось 2 раза.
Делай как нибудь, а как надо - само получится!

blade_007
ст. прапорщик
Сообщения: 571
Зарегистрирован: 2010-03-12 12:59:08
Контактная информация:

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение blade_007 » 2010-03-13 13:43:52

Иногда возникает необходимость в использовании нескольких баз аутентификации/пользователей одновременно. Например, у меня основные учетки почтовых аккаунтов хранятся в Active Directory, а в MySQL хранятся учетки для почтовых аккаунтов рассылок прайсов (никакого спама, только тем, кто соглашается), для которых создавать пользователя в Active Directory, прописывать ему почтовый адрес и задавать пароль как-то не правильно, хотя можно. Идею подбросил dikens3 здесь (http://forum.lissyara.su/viewtopic.php? ... 04#p227056).

Суть, вкратце:
1. Написать проверку в mysql пользователя/пароля
2. Написать проверку в AD пользователя/пароля
3. Совместить 1 и 2 в одном запросе.

Немного теории.
"Несколько условий могут быть проверены за один раз, объединенные операторами “and” и “or”. Условия “and” и “or” самостоятельные, полноценные условия, и предшествуют своим спискам субпаттернов. Каждое подусловие должно находиться внутри фигурных скобок, вместе с общими фигурными скобками, в которых находится список. Не должно быть повторений, если используется условие “if”." - взято из документации.

Комбинированные условия OR/AND принимают только условия, описанные в секции 11.7, т.е. crypteq, eq, ldapauth и т.п. Они возвращают булево значение (true/false). Если у вас аутентификатор использует какие-либо lookup - приведите его к соответствующему виду, например, {eq{${lookup ldap {LDAP_QUERY}{true}{false}}}{true}}.

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

Код: Выделить всё

# список серверов глобального каталога
ldap_default_servers = <; 10.1.*.*:3268 ; 172.27.*.*:3268
# макрос с описанием аккаунта, под которым будет соединяться exim с сервером глобального каталога
LDAP_AD_BINDDN =${quote_ldap:CN=exim,CN=Users,DC=*****,DC=*******,DC=*****,DC=com}
# макрос с описанием соответствующего пароля вышеуказанного аккаунта
LDAP_AD_PASS = 123-123
# макрос, описыващий базу поиска
LDAP_AD_BASE = DC=*******,DC=********,DC=*****,DC=com
# список хостов, которым отправляется приглашение о возможности аутентификации AUTH
auth_advertise_hosts = *
В секции с параметрами аутентификации вносим следующий код (следует помнить, что у каждого будут свои настройки):

Код: Выделить всё

  
# AUTH PLAIN authentication method used by Netscape Messenger. 
plain: 
  driver = plaintext 
  public_name = PLAIN 
  server_condition = ${if or {\
   {crypteq{$3} {${lookup mysql{SELECT crypt FROM users WHERE id = '${quote_mysql:${local_part:$2}}' AND passwd = '${quote_mysql:$auth3}' AND active = 'Y'}{$value}{*}}}}\
  {ldapauth {user=${quote_ldap:${lookup ldapdn {user=LDAP_AD_BINDDN pass=LDAP_AD_PASS \
  ldap:///LDAP_AD_BASE??sub?\
(&(objectclass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=${quote_ldap:$auth2}))}{$value}fail}} \
pass=${quote:$auth3} connect=5 ldap:///}} \
  } \
}
Структура таблицы users в БД MySQL

Код: Выделить всё

CREATE TABLE `users` (
  `id` varchar(64) NOT NULL DEFAULT '',
  `crypt` varchar(64) NOT NULL DEFAULT '',
  `passwd` varchar(64) NOT NULL DEFAULT '',
  `uid` int(10) NOT NULL DEFAULT '47',
  `gid` int(10) NOT NULL DEFAULT '12',
  `domain` varchar(128) NOT NULL DEFAULT '*********.ru',
  `shell` varchar(32) NOT NULL DEFAULT '/sbin/nologin',
  `home` varchar(128) NOT NULL DEFAULT '/var/spool/vmail/domains',
  `quota` tinyint(4) DEFAULT '15',
  `active` enum('Y','N') NOT NULL DEFAULT 'Y',
  PRIMARY KEY (`id`,`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Краткое описание аутентификатора:
В настройках пользовательских программ указаны аккаунт пользователя (в моем случае почтовый адрес) и пароль к нему. При попытке отправить сообщение через почтовый сервер пользователю прийдется авторизоваться, потому как у меня только авторизованные пользователи могут отправлять почту. Поиск пользователя производится сначала в БД MySQL (причем, если БД в данный момент недоступна, то аутентификация не производится вообще, возможно есть какой-то обходной путь, но пока я его не нашел), затем в глобальном каталоге Active Directory (сначала ищется незаблокированный пользователь по его почтовому адресу, затем попытка авторизоваться, используя найденное ранее имя пользователя и пароль к доменной учетке). Если любой из поисков успешен, то пользователь будет успешно авторизован.

Немного сумбурно, но, думаю, вполне доступно для понимания. Вообщем, здравая критика приветствуется.
PS: По аналогии можно описать и аутентификатор LOGIN.

deepgray
проходил мимо

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение deepgray » 2010-03-22 14:41:05

Liv писал(а): Удаление из очереди писем с пустым отправителем (<>) (назвал edele):

Код: Выделить всё

#!/usr/bin/perl

my $ar = "";

if ($#ARGV == -1) {
    $ar="q";
}
$ar= shift(@ARGV);

open ( MQ, "/usr/local/sbin/exim -bp|") or die "can't run the exim command\n";

$count = 0;
$state = 0;     # simple state machine
$recips = 0;
while ( <MQ> ) {
  $line = $_;
  chop $line;
  if($line =~ /\<\>/) {
        if($line =~ /(.*) (.*) (.*) \<\>/) {
                $ident = $3;
                system("exim -Mrm ". $ident);
                $count++;
        }
  }
  if($line =~ /\|/) {
        if($line =~ /(.*) (.*) (.*) (.*)/) {
                $ident = $3;
                system("exim -Mrm ". $ident);
                $count++;
        }
  }
}
close(MQ);

print "Deleting count: ". $count."\n";

# end
наверное проще так:

Код: Выделить всё

exim -bpr | grep "<>" |cut -c11-27|xargs exim -Mrm

Dystopian
проходил мимо
Сообщения: 2
Зарегистрирован: 2009-03-17 16:20:39

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение Dystopian » 2010-03-22 14:54:39

Думаю, в очереди писем с пустым отправителем (т.н. bounce) вообще не должно скапливаться. Пересылать их можно только своим (локальным) пользователям, а вот для чужих серверов они являются спамом. У себя решил эту проблему написанием роутера:

Код: Выделить всё

bounce_killer:
  senders = :
  domains = !+local_domains
  driver = redirect
  allow_fail
  data = :fail: bounce ignored
  no_more
  no_verify
Он должен быть расположен до роутера dnslookup.

viiri
проходил мимо
Сообщения: 4
Зарегистрирован: 2010-03-14 4:14:52

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение viiri » 2010-03-24 1:46:09

Отправка jabber-уведомлений

Код: Выделить всё

jabber:
    driver = pipe
    command = /bin/sh -c "echo 'You have an incoming mail from ${sender_address}\r\n' | /usr/local/bin/sendxmpp -f /usr/local/etc/.sendxmpprc -s 'Incoming mail' ${lookup mysql{SELECT `jid` FROM `jabber` WHERE `address`='${quote_mysql:$local_part@$domain}' AND active='1'}}" 
    user = mailnull
    group = mail
  
dovecot_delivery:
    driver = pipe
    command = /usr/local/libexec/dovecot/deliver -d $local_part@$domain
    message_prefix =
    message_suffix =
    delivery_date_add
    envelope_to_add
    return_path_add
    log_output
    user = mailnull
    shadow_transport = jabber

Аватара пользователя
bobot
ст. прапорщик
Сообщения: 589
Зарегистрирован: 2008-05-28 20:03:17

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение bobot » 2010-03-30 11:11:25

Вот рабочий автоответчик exim
Router:

Код: Выделить всё

userautoreply:
driver = accept
condition = ${if eq{} {${lookup mysql{SELECT recipient FROM autoreply \
WHERE recipient='${local_part}@${domain}'}}}{no}{yes}}
senders = " ! ^.*-request@.*:\
! ^owner-.*@.*:\
! ^postmaster@.*:\
! ^listmaster@.*:\
! ^mailer-daemon@.*\
! ^root@.*\
! ^noreply@.*"
no_expn
no_verify
transport = userautoreply
unseen

Транспорт:

Код: Выделить всё

userautoreply:
driver = autoreply
from = ${local_part}@${domain}
reply_to = ${local_part}@${domain}
to = ${sender_address}
subject = AutoReply Re: ${rfc2047:$h_Subject:}
text = ${lookup mysql{SELECT message FROM autoreply WHERE recipient='${local_part}@${domain}'}}
Mysql:

Код: Выделить всё

CREATE TABLE IF NOT EXISTS `autoreply` (
`id` int(100) NOT NULL AUTO_INCREMENT,
`recipient` varchar(50) NOT NULL DEFAULT '',
`message` longtext CHARACTER SET cp1251 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
И наслаждаемся 8)

viiri
проходил мимо
Сообщения: 4
Зарегистрирован: 2010-03-14 4:14:52

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение viiri » 2010-04-02 6:21:04

Алиасы по запросу.

Код: Выделить всё

alias_router:
  driver = accept
  domains = +local_domains
  local_part_prefix = alias-
  senders = ${lookup mysql{SELECT `username` FROM `mailbox` WHERE `username`='${quote_mysql:$sender_address}'}}
  condition = ${if and {{eq{${lookup mysql{SELECT `username` FROM `mailbox` WHERE `username`='${quote_mysql:$local_part@$sender_address_domain}'}}}{}}{eq{${lookup mysql{SELECT `address` FROM `alias` WHERE `address`='${quote_mysql:$local_part@$sender_address_domain}'}}}{}}}{yes}{no}}                                                                                                                    
  transport = alias_reply
  
alias_reply:
  driver = autoreply
  from = "${quote_mysql:$local_part@$domain}"
  to = "${sender_address}"                   
  subject = "Alias request."                 
  text = "Alias ${local_part}@${sender_address_domain} for ${sender_address} created."
  file = ${lookup mysql{INSERT INTO `alias` VALUES ('${quote_mysql:$local_part@$sender_address_domain}','${quote_mysql:${sender_address}}','${quote_mysql:$sender_address_domain}',now(),now(),'1')}}                                                                   
  file_optional = true
Автосоздание алиаса по отправке на ящик вида: alias-aliasname@domain.tld. Если прирутить временные метки, можно сделать сервис кратковременных почтовых ящиков.

ЗЫ: Код кривоват, если есть мысли по улучшению - делитесь.

gigabyte
проходил мимо

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение gigabyte » 2010-05-13 12:08:59

Удаление из очереди писем с пустым отправителем (<>) (назвал edele):

Код: Выделить всё

#!/usr/bin/perl

my $ar = "";

if ($#ARGV == -1) {
    $ar="q";
}
$ar= shift(@ARGV);

open ( MQ, "/usr/local/sbin/exim -bp|") or die "can't run the exim command\n";

$count = 0;
$state = 0;     # simple state machine
$recips = 0;
while ( <MQ> ) {
  $line = $_;
  chop $line;
  if($line =~ /\<\>/) {
        if($line =~ /(.*) (.*) (.*) \<\>/) {
                $ident = $3;
                system("exim -Mrm ". $ident);
                $count++;
        }
  }
  if($line =~ /\|/) {
        if($line =~ /(.*) (.*) (.*) (.*)/) {
                $ident = $3;
                system("exim -Mrm ". $ident);
                $count++;
        }
  }
}
close(MQ);

print "Deleting count: ". $count."\n";

# end
вполне заменяэтся командой:

Код: Выделить всё

 exim -bp | grep "<>" | awk '{ print $3 }' | xargs exim -Mrm 

smeegul
проходил мимо
Сообщения: 1
Зарегистрирован: 2010-05-28 10:35:52

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение smeegul » 2010-05-28 11:26:44

Не пропускаем почтовики в HELO/EHLO которых только буквы, цифры, -, _, пробел:

Код: Выделить всё

 deny    message       = "Can not be only letters, numbers, -, _, space in HELO/EHLO. See SMTP RFC"
          condition     = ${if match{$sender_helo_name}{\N^[A-Za-z0-9\_\-\ ]+$\N}{yes}{no}}
          !senders      = :
          !authenticated = *
          hosts         = !127.0.0.1 : !localhost : *
          logwrite      = "Can not be only letters, numbers, -, _, space in HELO/EHLO. See SMTP RFC: [SH=$sender_host_address S=$sender_address HELO=$sender_helo_name LPLD=$local_part@$domain]"
Не пропускаем если нет обратной зоны ДНС:

Код: Выделить всё

  deny    message       = "Inconsistent or no DNS reverse entry"
          !authenticated = *
          hosts         = !127.0.0.1 : !localhost : *
          condition = ${if and{{def:sender_host_address}{!def:sender_host_name}}{yes}{no}}
          logwrite      = "Inconsistent or no DNS reverse entry for $sender_host_address"
Не пропускаем если отправитель и получатель одинаковые, т.е. нельзя будет отправить со своего мыла себе письмо:

Код: Выделить всё

deny    message       = "Sender and recipient is equivalent"
          !senders      = :
          condition     = ${if eq{$sender_address}{$local_part@$domain}{yes}{no}}
          logwrite      = "Sender and recipient is equivalent. $sender_address = $local_part@$domain"

eem-kz
мл. сержант
Сообщения: 98
Зарегистрирован: 2010-05-02 15:58:53

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение eem-kz » 2010-05-28 14:57:28

Тут полезная статья: "Exim + Vexim + MySQL + Clamav + SpamAssassin + Greylisting + Whitelisting + Dovecot"
http://jared.kiev.ua/2010/01/isp-mailserver-1-exim/
Родной язык не русский. За это мне трудно изложит красиво. Поймите ...!

eem-kz
мл. сержант
Сообщения: 98
Зарегистрирован: 2010-05-02 15:58:53

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение eem-kz » 2010-06-01 9:26:20

статья по настройке (Exim с панелью управления VExim и imap сервером Dovecot.)

http://www.hilik.org.ua/exim-с-панелью- ... dovecot-ч/
Родной язык не русский. За это мне трудно изложит красиво. Поймите ...!

Аватара пользователя
ban
мл. сержант
Сообщения: 145
Зарегистрирован: 2009-07-22 22:36:29
Откуда: г.Волжский Волг. обл.

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение ban » 2010-07-03 12:09:40

SSL/TLS для защищенного общения SMTP-клиент <-> SMTP-сервер

Будьте внимательны.
При помощи SSL/TLS мы будем защищать общение: SMTP-клиент <-> SMTP-сервер, - в случае когда SMTP-клиент попытается использовать SMTP-сервер в качестве релея. Если не понятно, то посмотрите на картинку.

Минимум теории по SSL/TLS:
В SSL-wrapped соединении (например, в Thunderbird этот тип защиты звучит как "SSL/TLS") клиент подключается к другому порту (SMTPS, POP3S, IMAPS) и устанавливает SSL-сессию до начала работы основного протокола, так называемые Безопасные Сокеты. Только когда безопасное соединение установлено, сервер позволит клиенту начать работу по своему собственному протоколу (SMTP, IMAP, HTTP и т.д.) - поверх SSL/TLS протокола, обеспечивающего безопасность.

Однако, текущие версии Интернет протоколов поддерживают команды типа STARTTLS/STLS. При использовании STARTTLS, клиент устанавливает незащищенное соединение на стандартный порт (SMTP, POP3, IMAP и т.д.) и затем отправляет команду STARTTLS или ей подобную, и сервер с клиентом начинают безопасную SSL/TLS сессию. Если используемое вами программное обеспечение поддерживает команду STARTTLS, то вам не надо создавать специальные Безопасные Сокеты для безопасных (SSL/TLS) коммуникаций.

В обоих случаях, требуют наличие серверного ключа и сертификата, допускается размещения их в одном файле.


------------------- Перейдем к Exim -----------------------
В секцию MAiN конфигурации Exim:

Код: Выделить всё

tls_advertise_hosts  = *
tls_certificate      = /usr/local/etc/exim/smtp.freebsd-go-go.crt
tls_privatekey       = /usr/local/etc/exim/smtp.freebsd-go-go.key
здесь сертификат и ключ в разных файлах, если все будет в одном, то надо только указать tls_certificate.

Теперь добавить в секцию аутентификации каждому методу строку:

Код: Выделить всё

server_advertise_condition = ${if eq{$tls_cipher}{}{false}{true}}
и после этого никакому SMTP-клиенту не получится аутентифицироваться, если до аутентификации он не инициирует командой STARTTLS защищенную SSL/TLS сессию (надо будет принять сертификат).
При успехе данные между SMTP-клиентом и SMTP-сервером будут шифроваться и тогда можно будет начать аутентификацию.
--------------------------------------------

Создать сертификат и ключ в одном файле и не заморачиватся вообще ни с чем, например:

Код: Выделить всё

openssl req -newkey rsa:4096 -x509 -nodes -keyout /exim.cert -out /exim.cert -days 9999
НО для создания сертификатов я решил пользоваться вариантом с корневым сертификатом: SSL Howto, Creating and Using SSL Certificates.
кто никуда не торопится, тот везде успевает

Аватара пользователя
mastertron
мл. сержант
Сообщения: 107
Зарегистрирован: 2009-02-06 20:48:53
Откуда: Украина, Одесса

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение mastertron » 2010-09-23 12:51:31

Часто при связке с MySQL применяются всякого рода списки (белые,серые и т.д.), которые периодически нуно чистить ( или чет еще ). Многие пишут запрос в конфиге exim . У кого этот запрос происходит при каждом проходе, у кого сравнивается с временем... Решил периодические запросы вынести за пределы exim , в файл mail_sql, и cron-ом запускать. На всякий случай запросам с скрипта даю LOW_PROPRITY.

Код: Выделить всё

15       4      *       *       *       mysql mail -u exim -pexim  < /root/mail_sql
Еще вариант - много различных алгоритмов с данными в таблицах и просто запросов можно держать в хранимых процедурах. Единственный минус - если с процедуры нужно вернуть данные, то обращаться к базе нуно дважды, вызвать процедуру с передачей параметров и следом запросить SELECT переменной с результатом.
Думаю это здорово скажется на производительности загруженного почтовика, особенно если база размещена на другом хосте.
Делай как нибудь, а как надо - само получится!

mediamag
лейтенант
Сообщения: 693
Зарегистрирован: 2008-10-02 20:49:21

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение mediamag » 2010-11-18 23:29:15

Blacklist (ручной)

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

Код: Выделить всё

CREATE TABLE `user_blacklist` (
`id` bigint(20) NOT NULL auto_increment,
`relay_ip` char(15) default NULL,
`sender_domain` char(64) default NULL,
`recipient` char(64) default NULL,
`ehlo` char(64) default NULL,
PRIMARY KEY  (`id`)
) TYPE=MyISAM ;
Кондишн:

Код: Выделить всё

# Local user`s blacklist

  deny    message       = Mailbox of domain $sender_address_domain is in a personal blacklist of $local_part
          senders       = ${lookup mysql{SELECT `sender_domain` FROM `user_blacklist` WHERE `sender_domain` = '${quote_mysql:$sender_address_domain}' AND `recipient`='${quote_mysql:$local_part@$domain}'}}
          log_message   = Mailbox of domain $sender_address_domain is in a personal blacklist of $local_part
И ручками добавляем домен в колонки таблицы user_blacklist, либо так:

Код: Выделить всё

INSERT INTO `user_blacklist` VALUES ('1', null, 'baddomen.ru', 'admin@domen.ru', null);
baddomen.ru - домен блокируемого
admin@domen.ru - ящик, на кого блокировать.
Остальные колонки (id,ehlo,relay_ip) я создал для жесткой привязки ip>baddomen.ru, но пока этим не пользуюсь

P.S. Не проходите мимо, если у вас есть какая нить веб-морда или идеи по автоматизации блеклиста

Аватара пользователя
mastertron
мл. сержант
Сообщения: 107
Зарегистрирован: 2009-02-06 20:48:53
Откуда: Украина, Одесса

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение mastertron » 2010-11-24 23:14:46

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

Код: Выделить всё

acl_check_data:
         warn    condition =  ${if eq{spam@mydomain.ex}{${acl_m0}}{yes}{no}}
                 set acl_m19 = ${lc:$rheader_From:}
Переменную acl_m0 забиваем заранее:

Код: Выделить всё

acl_check_rcpt:
      warn       set acl_m0 = $local_part@$domain 
Так же заранее нужно забить, от кого получаем сообщение ( кто пересылает). Потом обрабатываем переменную $acl_m19 , в которой, как ясно, сидит адрес "От:", пихая что нужно от него в базу, и соответственно отправителя ( кто себе "заглушку" поставил). Однако! Тут есть два НО!
1 - в заголовке "От:" обратного адреса может вообще не быть.
2 - Адрес может быть голым ( spammer@spam.ygy ), а может содержать комментарий ( steep spammer Vasilii <spammer@spam.ygy> ).
С первым мне проще, если не указан на конверте отправитель, я его ваще не принимаю ( не по человечи это как-то). Со вторым ... тут строку разбирать нужно, проверять наличие угольных скобок и т.д. Я бы это сделал в хранимой процедуре на MySQL , чтоб только два параметра передать ей. Кто то возможно сплетет это дело на exim (perl) , но я в нем слаб. Вот така наметка, чем могу.
Делай как нибудь, а как надо - само получится!

Аватара пользователя
Laa
ст. лейтенант
Сообщения: 1032
Зарегистрирован: 2008-02-21 18:25:33
Откуда: Украина, Россия

Изменить порт для одного домена

Непрочитанное сообщение Laa » 2010-11-27 17:12:13

Привет.
Всплыла проблема: на один из почтовых серверов не ходит почта. Бился-бился, ну не ходит. Оказалось, что не ходит на 25 порт, а на другой открытый смтп порт 587 ходит на ура. Позже выяснилось, что проблема в иосе цыски по дороге была. Но решение уже было найдено.

Код: Выделить всё

remote_smtp:
  driver                = smtp
  port                 = ${if match{$domain}{домен.net.ua}{587} {25} }
Мало ли, может кому и пригодится. Решение простое и как обычно все в экзиме -- изящное.
exim: помните, что выдавая deny, вы можете недоставить ваше же письмо, зарубив sender-verify удаленного MTA к вашему MTA!!!

Аватара пользователя
mastertron
мл. сержант
Сообщения: 107
Зарегистрирован: 2009-02-06 20:48:53
Откуда: Украина, Одесса

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение mastertron » 2010-11-28 20:57:22

Народ, приношу свои извенения, что написал в предыдущем посту – бред. Я не знал, что клиент при пересылке письма пересоздает заголовок и созданные дополнительные поля просто исчезают (пенсионеру простительно). Но идею решил развить, используя поле, которое остается не тронутым при любом раскладе – тема письма. Плюс еще коечто, и того…
Управление списками (MySQL) с помощью почтовых ящиков.
Я, как и многие, использую контрольный ящик, на который копируются все проходящие через почтовик письма. Каждое утро просматриваю и оцениваю качество антиспамовой защиты и прохождения нормальной почты. Идеальной автоматической системы добиться трудно, посему вычисляю просочившийся через spamd и свою защиту спам и вручную забиваю IP «плохих» в черный список и иногда заполняю «хороших» к белым IP. Приоритет отдаю белым, поэтому запись в черные происходит через хранимую процедуру, которая сначала ищет этот IP среди белых, если находит – отбой.
Чтоб не заморачиватся лишними телодвижениями решил чуть сокротить путь и создать ящики, при перенаправлении на которые данные об хосте, с которого было принято письмо, автоматом зашивались в нужные списки (таблицы MySQL).
Начну по порядку.
Чтоб небыло трудностей в других блоках exim в начале забиваем нужные данные в переменные:

Код: Выделить всё

acl_check_rcpt:
 warn    set acl_m0 = $local_part@$domain
	    set acl_m1 = $sender_host_name
	    set acl_m3 = $sender_address
	    set acl_m4 = $sender_host_address
Контрольный ящик (копирование) организован в роутерах по стандартной схеме:

Код: Выделить всё

copy_message:
  condition  = ${if eq{control@мойдомен}{$acl_m3}{no}{yes}}
  driver = redirect
  unseen
  domains = *
  data = control@мой домен
Кондишен для того, чтоб исходящие письма с этого ящика не копировались.
Так как ящик spam@mydomain нужен только для манипуляций, то письма на него можно сразу убить подобным образом: (там же - следом)

Код: Выделить всё

delete_message:
  condition  = ${if eq{spam@мойдомен}{$acl_m0}{yes}{no}}
  driver = redirect
  unseen
  domains = *
  data = :blackhole:
Ну, понятно, что нужно создать ящики control@mydomain и ящик spam@mydomain.
Для хранения нужных данных о том, кто и откуда прислал на сервер письмо я использую поле заголовка Subject: , при этом тему письма другим пользователям не меняю, только для контрольного ящика. Для этого лепим дополнительный роутер предпоследним:

Код: Выделить всё

control:
  driver = accept
  condition  = ${if eq{control@мойдомен}{$local_part@$domain}{yes}{no}}
  headers_remove = subject
  headers_add = Subject: h_ip=$acl_m4, h_name=$acl_m1, s_addr=$acl_m3
  transport = mysql_delivery

mysqluser:
  …..
Результат: в теме письма имеем поля “h_ip” , “h_name” , “s_addr” и их значения.

Теперь осталось это использовать. Другим пользователям работать со стратегическими объектами я доверить не могу, посему доступ к операции только с контрольного ящика:

Код: Выделить всё

acl_check_data:

    accept	condition  = ${if eq{control@мойдомен}{$acl_m3}{yes}{no}}
#   		 hosts      = 192.168.0.2     
		condition  = ${if eq{spam@мойдомен}{$acl_m0}{yes}{no}}
		set acl_m19 = ${lookup mysql{INSERT INTO bad_ip (host_ip, set_date) VALUES ('${extract{h_ip}{$rheader_Subject:}}', CURDATE() )}}
		log_message = srabotalo, blya…
Ну понятно, что с помощью "${extract{h_ip}{$rheader_Subject:}}" можно вытягивать с любых переменных любые (заранее заложенные) значения.
Вместо «аccept» можно «warn», но на время отладки лучше оставить (логи информативнее).
Суть, думаю, ясна. Подобным образом можно управлять не одним списком в базе.
Можно сделать ящики «Я в отпуске», «Я вернулся» для пользователей, личный блок-лист, ну и т.д.

(чисто для связи с примером) таблица блокируемых IP.

Код: Выделить всё

CREATE TABLE mail.bad_ip(
  id INT(11) NOT NULL AUTO_INCREMENT,
  host_IP VARCHAR(20) DEFAULT NULL,
  set_date DATE DEFAULT NULL,
  PRIMARY KEY (id)
)
На идеал реализации конечно же не тянет, но идея воплотима. Чую, что можно чего-то сократить, если есть замечания/предложения - дайте.
Делай как нибудь, а как надо - само получится!

Аватара пользователя
mastertron
мл. сержант
Сообщения: 107
Зарегистрирован: 2009-02-06 20:48:53
Откуда: Украина, Одесса

Re: Полезные скрипты/настройки для exim.

Непрочитанное сообщение mastertron » 2010-11-29 13:08:45

Немного доработки:
Для удаления сообщений, приходящих на несколько ящиков можно так:

Код: Выделить всё

delete_message:
  domains = домен_ящиков
  local_parts = block_ip : white_ip : spam : ......
  driver = redirect
  unseen
  data = :blackhole:
Не актуально, но ...
поменьше информации в исходящих сообщениях

Код: Выделить всё

dnslookup:
  driver = dnslookup
  ......
  headers_remove = received
Добавив вконце данного роутера удаление всех полей Received: не показываем структуру внутренней сети.
Делай как нибудь, а как надо - само получится!