Защита dbmail с помощью fail2ban
Пару дней назад, по чистой случайности заметил на одном из серверов подозрительную активность. А именно, кто-то брутил pop3 авторизацию, атака шла с одного ip поэтому я забанил его, написал владельцу подсети и благополучно забыл об этом..
Делается это такой командой:
1 2 3 |
# iptables -I INPUT -s 255.255.255.255 -j DROP |
где, 255.255.255.255 - ip адрес атакующего.
Сегодня решил проверить как там обстоят дела.. К сожалению, атакующий не успокоился, сменил ip и продолжил свое черное дело..
В логах dbmail-а, были вот такие записи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
... Aug 04 14:24:02 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [christopher] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:02 mysite.com dbmail-pop3d[16280]: Error:[pop3] pop3.c,pop3(+407): user [client] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:03 mysite.com dbmail-pop3d[16280]: Error:[pop3] pop3.c,pop3(+407): user [clayton] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:03 mysite.com dbmail-pop3d[16246]: Error:[pop3] pop3.c,pop3(+407): user [chuck] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:04 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [client] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:04 mysite.com dbmail-pop3d[16246]: Error:[pop3] pop3.c,pop3(+407): user [chuck] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [clayton] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [chuck] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[10492]: Error:[pop3] pop3.c,pop3(+407): user [christopher] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[16246]: Error:[pop3] pop3.c,pop3(+407): user [clayton] coming from [255.255.255.255] tried to login with wrong password123 ... |
Т.к. я в первую очередь программист, то решил написать скрипт, который будет парсить лог и банить атакующих. Это сделать было просто, т.к. у всех кто пользовался почтой на этом сервере, были постоянные и известные мне ip адреса, поэтому я мог применить фильтрацию "забанить всех, кроме разрешенных".. Через пол часа был готов простенький скрипт:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
<?php class cfg { public static $root_dir='./'; // список разрешенных ip public static $allowed_ips = array( 'ВАШ.IP.АДР.ЕС', ); // путь к логу dbmail public static $dbmail_log_path = '/var/log/dbmail/dbmail.err'; // кол-во строк с конца которое будет анализироваться public static $dbmail_read_last_lines_cnt = 500; // если встретится в выборке кол-во ip больше чем // установлено тут, ip будет считаться атакующим public static $ban_if_found_more_than = 5; } // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cfg::$root_dir = dirname(__FILE__).'/'; $malefactor_ips = dbmail_get_malefactor_ips(); $banned_ips = get_banned_ips(); $banned_ips = combine_banned_and_malefactor($banned_ips, $malefactor_ips); file_put_contents(cfg::$root_dir.'banned_ip.txt', implode(PHP_EOL,$banned_ips)); if ($banned_ips) { $ipt_banned_ips = ipt_get_banned_ips(); foreach ($banned_ips as $ip) { if (!in_array($ip, $ipt_banned_ips)) { ipt_ban_ip($ip); echo 'IP: '.$ip.' banned'.PHP_EOL; } } } $blocked_ips = ipt_get_banned_ips(); echo '====================================='.PHP_EOL; echo 'IP\'s blocked in iptables:'.PHP_EOL; foreach ($blocked_ips as $ip) echo '-> '.$ip.PHP_EOL; echo '====================================='.PHP_EOL; // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; function mexec($cmd) { $result = exec("$cmd 2>&1", $out, $err); return array( 'result' => $result, 'out' => $out, 'err' => $err, ); } function dbmail_get_malefactor_ips() { $malefactor = array(); $cmd = 'tail -n '.cfg::$dbmail_read_last_lines_cnt.' '.cfg::$dbmail_log_path; $res = mexec($cmd); if (is_array($res['out'])) { foreach ($res['out'] as $line) { if (preg_match('~user.+\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\].+tried\sto\slogin~', $line, $regs)) { $ip = $regs[1]; if (in_array($ip, cfg::$allowed_ips)) continue; if (!isset($malefactor[$ip])) $malefactor[$ip] = 0; $malefactor[$regs[1]]++; } } } foreach ($malefactor as $ip=>$cnt) if ($cnt<cfg::$ban_if_found_more_than) unset($malefactor[$ip]); return $malefactor; } function get_banned_ips() { $banned_ips = array(); if (file_exists(cfg::$root_dir.'banned_ip.txt')) { $banned_ips = file(cfg::$root_dir.'banned_ip.txt'); } return $banned_ips; } function combine_banned_and_malefactor(array $banned_ips, array $malefactor_ips) { foreach ($malefactor_ips as $ip=>$cnt) { if (!in_array($ip, $banned_ips)) $banned_ips[] = $ip; } foreach ($banned_ips as $k=>$ip) if (!trim($ip)) unset($banned_ips[$k]); return $banned_ips; } function ipt_get_banned_ips() { $banned_in_iptables = array(); $cmd = 'iptables -L INPUT -v -n'; $res = mexec($cmd); if (is_array($res['out'])) { foreach ($res['out'] as $line) { // pkts bytes target prot opt in out source destination // 0 0 DROP all -- * * 12.232.164.2 0.0.0.0/0 if (preg_match('~DROP.+\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+0\.0\.0\.0/0~', $line, $regs)) { $ip = trim($regs[1]); $banned_in_iptables[$ip] = $ip; } } } return $banned_in_iptables; } function ipt_ban_ip($ip) { $cmd = 'iptables -I INPUT -s '.$ip.' -j DROP'; $res = mexec($cmd); } |
Вкратце, скрипт работает так: берет из лога последние 500 строк, ищет в них ip адреса по маске, далее если ip адрес встретился больше чем 5 раз за выборку, он банится с помощью iptables.
Такой скрипт надо поставить в крон от рута на запуск раз в 15 минут, и проблема с атакующими решена. Во всяком случае, от брутфорса защита будет работать отлично.
Разумеется, сам скрипт можно быстро переписать вообще под любой анализ логов и любое действие, принцип один и тот же. Именно поэтому я его и выложил здесь.
Поигравшись чуть чуть со своим скриптом, я вспомнил, что у меня на сервере установлен fail2ban, и он уже успешно защищает ssh и ftp от всяких слоупоков. Я никогда раньше не писал фильтры для fail2ban, однако покурив ман и посмотрев заготовленные конфиги, за 5 минут написал правило для всё тех же действий - выдачу банов атакующим. Итак, первое что нужно сделать, это создать файл фильтра:
1 2 3 |
# nano /etc/fail2ban/filter.d/dbmail.conf |
В него записываем следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# Fail2Ban configuration file for dbmail # [Definition] # Option: failregex # Notes.: regex to match the password failures messages in the logfile. # Values: TEXT # failregex = user \[.+\] coming from \[<HOST>\] tried to login with wrong password$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT # ignoreregex = (127\.0\.0\.1|localhost) |
Тут есть всего два интересующих нас параметра:
- failregex - регулярка, для поиска IP адресов. IP адрес заменяется на директиву <HOST>
- ignoreregex - регулярка позволяющая исключить некоторые строки по подстроке
Если Вам понадобится что-то посложнее, посмотрите другие фильтры в папке:
/etc/fail2ban/filter.d/
Теперь можно протестировать наш фильтр на конкретном лог файле, делается это так:
1 2 3 |
# fail2ban-regex /var/log/dbmail/dbmail.err /etc/fail2ban/filter.d/dbmail.conf |
Если все ок, нам покажут простыню текста, типа этого:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
Running tests ============= Use regex file : dbmail.conf Use log file : /var/log/dbmail/dbmail.err Results ======= Failregex |- Regular expressions: | [1] user \[.+\] coming from \[<HOST>\] tried to login with wrong password$ | `- Number of matches: [1] 142 match(es) Ignoreregex |- Regular expressions: | `- Number of matches: Summary ======= Addresses found: [1] 255.255.255.255 (Sun Aug 04 14:24:02 2013) 255.255.255.255 (Sun Aug 04 14:24:02 2013) ... 255.255.255.255 (Sun Aug 04 14:29:27 2013) 255.255.255.255 (Sun Aug 04 14:29:27 2013) Date template hits: 343 hit(s): MONTH Day Hour:Minute:Second 0 hit(s): WEEKDAY MONTH Day Hour:Minute:Second Year 0 hit(s): WEEKDAY MONTH Day Hour:Minute:Second 0 hit(s): Year/Month/Day Hour:Minute:Second 0 hit(s): Day/Month/Year Hour:Minute:Second 0 hit(s): Day/Month/Year Hour:Minute:Second 0 hit(s): Day/MONTH/Year:Hour:Minute:Second 0 hit(s): Month/Day/Year:Hour:Minute:Second 0 hit(s): Year-Month-Day Hour:Minute:Second 0 hit(s): Day-MONTH-Year Hour:Minute:Second[.Millisecond] 0 hit(s): Day-Month-Year Hour:Minute:Second 0 hit(s): TAI64N 0 hit(s): Epoch 0 hit(s): ISO 8601 0 hit(s): Hour:Minute:Second 0 hit(s): <Month/Day/Year@Hour:Minute:Second> Success, the total number of match is 142 However, look at the above section 'Running tests' which could contain important information. |
Когда мы убедились что регулярка работает, добавляем наш фильтр в конфиг fail2ban-а:
1 2 3 |
# nano /etc/fail2ban/jail.conf |
Нужно добавить такой текст в конец:
1 2 3 4 5 6 7 8 9 |
[dbmail] enabled = true port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s filter = dbmail bantime = 86400 logpath = /var/log/dbmail/dbmail.err maxretry = 3 |
Параметры описывать не буду, по их названиям и так все ясно, да и гугл работает.
После этих изменений перезапускаем fail2ban:
1 2 3 |
# service fail2ban restart |
в моем случае в логе (/var/log/fail2ban.log), сразу появилась запись:
1 2 3 |
2013-08-04 14:29:27,877 fail2ban.actions: WARNING [dbmail] Ban 255.255.255.255 |
Что говорит о том, что атакующий забанен.
Как показывает практика, любую задачу можно решать разными способами. Изобретать велосипеды, конечно, занимательно, но намного дольше чем использовать готовые инструменты.
Author: | Tags: /
| Rating:
Leave a Reply