Мы — долго запрягаем, быстро ездим, и сильно тормозим.

FreeBSD
  настройка
  подсчёт трафика
  программы
  почтовые системы
  FreeBSD Mail Howto
  exim & dovecot
  exim & courier-imap
  squirrelmail
  exim + saslauthd + courier-imap
  postfix -> exim
  sendmail -> exim
  imapsync
  Postfix + LDAP
  maildrop & postfix
  DSPAM
  Exim + LDAP
  ISPmanager
  Backup MX
  exim + exchange
  exim + dovecot + win2003 AD
  RoundCube
  qmail-ldap + AD
  spamooborona
  exim&dovecot + fetchmail + SSL
  Postfix + DBMail
  Mailgraph
  smfsav
  Exim+PgSQL
  Postfix + Dovecot + Clamav + SpamAssasin + LDAP vs MYSQL
  Simplemail Admin
  MTA qmail full install
  OpenLDAP адресная книга
  POSTFIX Статистики
  Backup MX (exim)
  Exim + dovecot + PgSQL + web
  Exim+dovecot2+dspam
  Возможности Dovecot 2
  Dovecot2 configfiles RUS
  Почтовый сервер по шагам ч.1
  Почтовый сервер по шагам ч.2
  Почтовый сервер по шагам ч.3
  Шелезяки
  Мелочи
  Файловая система
  WWW
  Security
  system
  Games Servers
  X11
  Programming
Очумелые Ручки
OpenBSD
Cisco


www.lissyara.su —> статьи —> FreeBSD —> почтовые системы —> Защита от DDoS

Попытка защитить свой почтовый сервер от DDoS атаки.

Автор: buryanov.


Друг попросил о помощи, его почтовик подвергся ddos атаке. У меня были написаны скрипты, но у меня не настолько большая нагрузка, чтобы можно было их потестить по нормальному, да и времени тоже не было. Навеяло на их написание, точнее дописание обсуждение Защита exim от DDoS. А тут как раз и повод появился.

В моём случае почтовый сервер и почтовый релей - это два разных сервера, но, если это один и тот же сервер - то всё работает также прекрасно, что и было доказано.

Топология:
- пограничный маршрутизатор: FreeBSD 7.2/i386, Atom c 1 сетевой картой, зато с кучей vlan
- почтовик, он же и прокся, он же и MySQL сервер: FreeBSD 7.2/amd64, Core2Duo E8500, 8G Ram

Теперь по порядку:
8 - 10 тысяч одновременных конектов к exim, при условии, что в офисе работает 50 человек. Это уже слишком. Попахивает DDos'ом.
Ддосили smtp, imap не юзается, точнее юзается imaps, а его, почему-то, не ддосят.

Перед какими либо действиями обновляем порты, я это делаю с помощью cvsup.
[root@mx ]# cd /usr/ports/net/cvsup-without-gui
[root@mx ]# make install clean

Создаём к ниму конфиг-файл /usr/local/etc/cvsup/ports-sup
*default host=cvsup3.uk.freebsd.org
*default base=/var/db
*default prefix=/usr
*default release=cvs tag=.
*default delete use-rel-suffix
*default compress
ports-all

После чего командой
cvsup -g -L 2 /usr/local/etc/cvsup/ports-sup

обновляем порты.

Ставим Mysql:

[root@mx /usr/ports]# cd /usr/ports/databases/mysql51-server/
[root@mx ]# make install clean
[root@mx ]# echo 'mysql_enable="YES"' >> /etc/rc.conf

В конфиге указывываем побольше количество коннектов, параметр max_connections=1000 в секции [mysqld]. В моём случае это было 10000. По умолчанию файл конфигурации my.cnf будет находится в каталоге с самой базой /var/db/mysql, но пока мы не инициализируем mysql, там ничего не будет. Это можно сделать несколькими способами, один из них, запустить mysql, он сам всё сделает, затем отредактировать конфигурационный файл и перезапустить mysql. Запуск осуществляется командой
[root@mx /var/db]# /usr/local/etc/rc.d/mysql-server start

Более детально установка MySQL описана в статье Установка MySQL Автор Lissyara.

Ставим exim:
[root@mx /var/db]# cd /usr/ports/mail/exim
[root@mx /usr/ports/mail/exim]# make install clean
[root@mx ]# echo 'exim_enable="YES"' >> /etc/rc.conf

На этом сайте приведено множество примеров конфигурирования exim под разные цели: exim & dovecot, exim + exchange автор Lissyara, мои конфиги базируются на этих статьях, поэтому повторяться не буду, напишу только свои дополнения:
В файле конфигурации /usr/local/etc/exim/configure, если ваш файл разбит на несколько файлов, то в соответвтвующих файлах, параметр
# Максимальное число одновременных подключений по
# SMTP. Рассчитывать надо исходя из нагрузки на сервер
smtp_accept_max = 50

необходимо сделать побольше, иначе ваш exim буквально сразу захлебнётся. Перед begin acl создаём макрос:
MYSQL_IPFW     =       INSERT INTO `ipfw` \
(`ip`, `date`, `type`, `coun`,`email`) VALUES \
  ('$sender_host_address', now(), '$acl_m18', '1', '$sender_address')

Далее в разделе acl, создаём переменные:
        warn    set acl_m18     =       0
        warn    set acl_m19     =       0

и добавляем в секции проверок, в которых идут следующие  запреты:
        set acl_m18     = HELO1
        set acl_m19     = ${lookup mysql{MYSQL_IPFW}}

В разделе на проверки на EHLO/HELO, для каждой проверки можно присваивать свои значения $acl_m18, HELO1,HELO2,HELO10, чтобы потом отслеживать , где попался

        set acl_m18     = DNSRBL
        set acl_m19   = ${lookup mysql{MYSQL_IPFW}}

вносим, на проверке на RBL

        set acl_m18     = UnUser
        set acl_m19   = ${lookup mysql{MYSQL_IPFW}}

вносим, за попытку отправить почту несуществующим пользователям.
Создаём таблицы в MySQL, у меня exim использует базу exim_db, если у вас другая, то необходимо исправить на свою:
-- Создаём таблицу для занесения статистики
CREATE TABLE ipfw(
  ip CHAR (15) NOT NULL,
  `date` CHAR (19) NOT NULL,
  type CHAR (9) NOT NULL,
  coun INT (8) NOT NULL,
  email VARCHAR (64) DEFAULT NULL,
  INDEX `date` USING BTREE (`date` (13)),
  INDEX ip USING BTREE (ip),
  INDEX type USING BTREE (type)
)
ENGINE = MYISAM
CHARACTER SET latin1
COLLATE latin1_swedish_ci;

-- Создаём таблицы для анализа и занесения результатов
CREATE TABLE ddos_analis(
  ip CHAR (15),
  type CHAR (9) DEFAULT NULL,
  date_in CHAR (19) DEFAULT NULL,
  date_last CHAR (19) DEFAULT NULL,
  date_exp CHAR (19) DEFAULT NULL,
  coun INT (8) UNSIGNED DEFAULT NULL,
  PRIMARY KEY (ip),
  UNIQUE INDEX ip_type USING BTREE (ip, type)
)
ENGINE = MEMORY
CHARACTER SET koi8r
COLLATE koi8r_general_ci;

CREATE TABLE ddos_exp(
  ip CHAR (15) NOT NULL,
  date_delete CHAR (19) DEFAULT NULL,
  active CHAR (1) DEFAULT '0',
  UNIQUE INDEX ip USING HASH (ip)
)
ENGINE = MEMORY
CHARACTER SET koi8r
COLLATE koi8r_general_ci;

Теперь создаём логику, которая и будет почти всё делать
CREATE
TRIGGER ddos_analis1
AFTER INSERT
ON ipfw
FOR EACH ROW
BEGIN
  SET @coun = new.coun + 1;
  SET @ip = new.ip;
  SET @type = new.type;
  -- Время первого заноса в таблицу
  SET @date_in = new.`date`;
  -- Время последнего заноса в таблицу
  SET @date_last = new.`date`;
  -- Интервал, за который идёт анализ
  SET @date_exp = NOW() + INTERVAL 5 MINUTE;
  -- Интервал, для чего-то создавался, мож быть где-то пригодиться
  -- SET @date_delete = NOW() + INTERVAL 1 DAY;

  -- Заносим данные в таблицу колектор и, в случаи нахождения ip,
  -- обновляем и добавляем очки
  INSERT
  INTO exim_db.ddos_analis (ip, type, date_in, date_last, date_exp, coun)
  VALUES (@ip, @type, @date_in, @date_last, @date_exp, 1)
  ON DUPLICATE KEY
  UPDATE
    exim_db.ddos_analis.coun = exim_db.ddos_analis.coun + 1,
    exim_db.ddos_analis.date_last = @date_last,
    exim_db.ddos_analis.date_exp = @date_exp;
  
  -- Заносим данные в таблицу, из которой информацию черпает фаервол
  -- Данные заносятся из таблицы коллектора
  -- coun >10 - это условие, при котором можно с уверенностью сказать,
  -- что вас или спамят или ддосят, интервал анализа - @date_exp
  -- date_exp + INTERVAL 10 MINUTE интервал, 
  -- сколько в фаерволе будет находится ip
  INSERT IGNORE
  INTO exim_db.ddos_exp (ip, date_delete)
  SELECT
    ip, date_exp + INTERVAL 10 MINUTE
  FROM
    exim_db.ddos_analis
  WHERE
    coun > 10;

  -- Удаляем просроченные записи или записи из таблицы коллектора
  -- по времени оканчания срока жизни @date_exp
  DELETE
  FROM
    exim_db.ddos_analis
  WHERE
    LEFT(date_exp, 16) < NOW();

  -- Удаляем из таблицы коллектора свои подсети
  -- Надо будет переделать на свой whitelist, хранимый в мускуле
  DELETE
  FROM
    exim_db.ddos_analis
  WHERE
    ip LIKE '10.4.%' OR ip LIKE '192.168.%';
END

В триггере присутствуют комментарии, поэтому писать не буду ничего

Создаём скрипт на маршрутизаторе add_ipfw.sh:
#!/bin/sh

/usr/local/bin/mysql --host=ip_you_mysql_server \
--password='xх' --user=user_to_access_to_mysql_server exim_db --execute=' \

update exim_db.ddos_exp \
    set active = 2 \
  WHERE \
    LEFT(date_delete, 16) < NOW() and active = 1; \

UPDATE exim.ddos_exp \
        SET active = 5 where active = 0; \

SELECT "ipfw table 125 add " asdf, ip FROM exim_db.ddos_exp \
    WHERE \
      active = 5; \

SELECT "ipfw table 125 delete " asdf, ip FROM exim_db.ddos_exp \
    WHERE \
      active = 2; \

UPDATE exim_db.ddos_exp \
    SET active = 1 where active = 5; \

delete FROM exim_db.ddos_exp \
  WHERE \
    LEFT(date_delete, 16) < NOW() and active = 2;'| \
grep -v asdf>/tmp/exim_ipfw_add_table_blacklist.sh

. /tmp/exim_ipfw_add_table_blacklist.sh

rm  /tmp/exim_ipfw_add_table_blacklist.sh

Делаем скрипт выполнимым
[root@hqgw1 ]$ chmod +x add_ipfw.sh

Комментарии: в таблице ddos_exp есть поле active, которое отвечает за состояние ip в фаерволе, может принимать 3 значения:
0 - ip ожидают вноса в таблицу фаервола
1 - данные внесены
2 - данные ожидают удаления из фаервола

У меня используется ipfw. В крон root добавляем
* * * * * /путь/к/вашему/скрипту/add_ipfw.sh

В фаер добавляем:
#deny mail
${fwcmd} add 56 deny log ip from table\(125\) to any 25 via ${ifwan}

.
.
.
.
Следующей немаловажной защитой является защита от так называемых пустых коннекшинов, трафик как бы не идёт, но слот занят и потом отваливается с записью в логе
2009-09-07 15:12:42 [40591] SMTP connection from [91.78.180.232]:1666
I=[217.112.209.34]:25 lost
2009-09-07 15:12:42 [40591] no MAIL in SMTP connection from [91.78.180.232]:1666
I=[217.112.209.34]:25 D=4m38s

С помощью конфига Exim, я не нашел способа бороться, но у меня есть замечательный файл /var/log/all.log, в который пишется весь(почти) лог. Создаётся он так: в файле /etc/syslog.conf раскомментируется строка:
*.*     /var/log/all.log

затем
touch /var/log/all.log
/etc/rc.d/syslogd restart

Этот файл у меня и так парсится на предмет перебора паролей на ftp, ssh. Так пусть и exim'у он тоже поможет.

Создаём таблицу для занесения статистики:
CREATE TABLE ddos_nomail(
  `data` VARCHAR (255) DEFAULT NULL
)
ENGINE = MYISAM
CHARACTER SET koi8r
COLLATE koi8r_general_ci;

Создаём логику:
CREATE
TRIGGER trigger_nomail
AFTER INSERT
ON ddos_nomail
FOR EACH ROW
BEGIN

  SET @ip = new.`data`;
  SET @type = "noMAIL";
  -- Время первого заноса в таблицу
  SET @date_in = NOW();
  -- Время последнего заноса в таблицу
  SET @date_last = NOW();
  -- Интервал, за который идёт анализ
  SET @date_exp = NOW() + INTERVAL 5 MINUTE;
  -- Интервал, для чего-то создавался, мож быть где-то пригодиться
  -- SET @date_delete = NOW() + INTERVAL 1 DAY;

  -- Заносим данные в таблицу коллектор и, в случае нахождения ip, 
  -- обновляем и добавляем очки
  INSERT
  INTO exim_db.ddos_analis (ip, type, date_in, date_last, date_exp, coun)
  VALUES (SUBSTRING_INDEX(SUBSTRING_INDEX(@ip, "]:", 2), "[", -1), 
@type, @date_in, @date_last, @date_exp, 1)
  ON DUPLICATE KEY
  UPDATE
    exim_db.ddos_analis.coun = exim_db.ddos_analis.coun + 1,
    exim_db.ddos_analis.date_last = @date_last,
    exim_db.ddos_analis.date_exp = @date_exp;

  -- Заносим данные в таблицу, из которой информацию черпает фаервол
  -- Данные заносятся из таблицы коллектора
  -- coun >10 - это условие, при котором можно с уверенностью сказать,
  -- что вас или спамят или ддосят, интервал анализа - @date_exp
  -- date_exp + INTERVAL 10 MINUTE интервал, 
  -- сколько в фаерволе будет находится ip
  INSERT IGNORE
  INTO exim_db.ddos_exp (ip, date_delete)
  SELECT
    ip, date_exp + INTERVAL 10 MINUTE
  FROM
    exim_db.ddos_analis
  WHERE
    coun > 10;

  -- Удаляем просроченные записи или записи из таблици коллектора
  -- по времени окончания срока жизни @date_exp
  DELETE
  FROM
    exim_db.ddos_analis
  WHERE
    LEFT(date_exp, 16) < NOW();

  -- Удаляем из таблицы коллектора свои подсети
  -- Надо будет переделать на свой whitelist, хранимый в мускуле
  DELETE
  FROM
    exim_db.ddos_analis
  WHERE
    ip LIKE '10.4.%' OR ip LIKE '192.168.%';


END

Создаём очередной скрипт all_log_pars.sh
#!/bin/sh
msql_e="/usr/local/bin/mysql --user=exim_ins --password=х \
--database=exim_db --host=12.12.12.12"
lf="/var/log/all.log"

cp ${lf} ${lf}.tmp
> ${lf}
cat ${lf}.tmp >> ${lf}.store

## Exim analis for no MAIL in SMTP connection

cat ${lf}.tmp|grep 'no MAIL in SMTP connection'| \
awk '{print "insert into exim_db.ddos_nomail value (\""$0"\");"}' \
|${msql_e}
rm -f ${lf}.tmp

и делаем его исполнимым
chmod +x all_log_pars.sh

В root cron добавляем его выполнение по расписанию.
* * * * * /путь/к/вашему/скрипту/all_log_pars.sh

Данный метод анализа и защиты использует таблицы и скрипт из первой части статьи.

Следует обратить внимание, что у таблиц ddos_exp и ddos_analis тип MEMORY, нечего насиловать винт, для временных таблиц.



Ссылка на обсуждение: http://forum.lissyara.su/viewtopic.php?f=14&t=20665.

размещено: 2009-09-28,
последнее обновление: 2010-10-07,
автор: buryanov

оценить статью:

opt1k, 2009-11-27 в 11:04:11

начал читать - увидел cvsup.
Ув. автор, я думаю надо срочно исправить его на csup.

opt1k, 2009-11-27 в 11:10:32

А в целом статья хорошая, спасибо, пиши ещё :)

German, 2010-08-20 в 19:46:03

Хотелось бы "добавки" в плане прикручивания exim'a к pf'у(ведь не все пользуют ipfw)

buryanov, 2010-09-14 в 16:58:36

к сожалению, или к счастью, с pf\"ом не знаком, можбыть стоит посмотреть в его сторону, а можбыть ну его, мне и ipfw вполне хватает. Если решусь - то напишу обязательно

Garison, 2010-09-16 в 16:18:46

для новичков типа меня перед добалением триггеров необходимо сделать команду смены окончания команды
DELIMITER |
после END сделать |
и поменять обратно
DELIMITER ;

Олег М., 2013-10-23 в 4:21:37

Перепишите пожалуйста кто может тригеры под MySQL 5.5.34, а то выдает ошибку:
SQL-запрос:
CREATE TRIGGER ddos_analis AFTER INSERT ON ipfw
FOR EACH
ROW
BEGIN
SET @coun = new.coun +1;

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 7

Заранее огромнейшее спасибо тому кто перепишет.


Оставьте свой комментарий:
Ваше имя:   *
e-mail:  
жирный
наклонный
подчёркнутый
ссылка
цвет
Нынешний год:   *
 


Хостинг HOST-FOOD

2014-07-27, lissyara
gmirror

Удалённое создание софтверного зеркала средствами gmirror, на диске разбитом с использованием gpart. Использование меток дисков для монтирования разделов.
2013-08-20, zentarim
Scan+Print server FreeBSD 9

Настройка сервера печати и сервера сканирования под управлением операционной системы FreebSD 9 для МФУ Canon PIXMA MP540
2011-11-20, BlackCat
Разъём на WiFi-карту

Делаем съёмной несъёмную антену на WiFi-карте путём установки ВЧ-разъёма
2011-09-14, manefesto
Настройка git+gitosis

Настройка системы контроля версия исходного кода в связке git+gitosis+ssh
подписка

    вверх      
Статистика сайта
Сейчас на сайте находится: 10 чел.
За последние 30 мин было: 44 человек
За сегодня было
8204 показов,
1059 уникальных IP
 

  Этот информационный блок появился по той простой причине, что многие считают нормальным, брать чужую информацию не уведомляя автора (что не так страшно), и не оставляя линк на оригинал и автора — что более существенно. Я не против распространения информации — только за. Только условие простое — извольте подписывать автора, и оставлять линк на оригинальную страницу в виде прямой, активной, нескриптовой, незакрытой от индексирования, и не запрещенной для следования роботов ссылки.
  Если соизволите поставить автора в известность — то вообще почёт вам и уважение.

© lissyara 2006-10-24 08:47 MSK

Время генерации страницы 0.1245 секунд
Из них PHP: 37%; SQL: 63%; Число SQL-запросов: 77 шт.
Исходный размер: 118286; Сжатая: 21193