|  |    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 раскомментируется строка:
 затем
 
		
| 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
 |  и делаем его исполнимым
 В root cron добавляем его выполнение по расписанию.
 
		
| * * * * * /путь/к/вашему/скрипту/all_log_pars.sh
 |  Данный метод анализа и защиты использует таблицы и скрипт из первой части статьи.
 Следует обратить внимание, что у таблиц ddos_exp и ddos_analis тип MEMORY, нечего насиловать винт, для временных таблиц.
 
 
 размещено: 2009-09-28,последнее обновление: 2010-10-07,
 автор: buryanov
 
 |  | 
	
Комментарии пользователей [6 шт.]