X

Защита dbmail с помощью fail2ban

Пару дней назад, по чистой случайности заметил на одном из серверов подозрительную активность. А именно, кто-то брутил pop3 авторизацию, атака шла с одного ip поэтому я забанил его, написал владельцу подсети и благополучно забыл об этом..

Делается это такой командой:

# iptables -I INPUT -s 255.255.255.255 -j DROP

где, 255.255.255.255 - ip адрес атакующего.

Сегодня решил проверить как там обстоят дела.. К сожалению, атакующий не успокоился, сменил ip и продолжил свое черное дело..

В логах dbmail-а, были вот такие записи:

...
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 адреса, поэтому я мог применить фильтрацию "забанить всех, кроме разрешенных".. Через пол часа был готов простенький скрипт:

<?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 минут написал правило для всё тех же действий - выдачу банов атакующим. Итак, первое что нужно сделать, это создать файл фильтра:

# nano /etc/fail2ban/filter.d/dbmail.conf

В него записываем следующее:

# 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/

Теперь можно протестировать наш фильтр на конкретном лог файле, делается это так:

# fail2ban-regex /var/log/dbmail/dbmail.err /etc/fail2ban/filter.d/dbmail.conf

Если все ок, нам покажут простыню текста, типа этого:

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-а:

# nano /etc/fail2ban/jail.conf

Нужно добавить такой текст в конец:

[dbmail]
enabled = true
port    = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s
filter  = dbmail
bantime = 86400
logpath = /var/log/dbmail/dbmail.err
maxretry = 3

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

После этих изменений перезапускаем fail2ban:

# service fail2ban restart

в моем случае в логе (/var/log/fail2ban.log), сразу появилась запись:

2013-08-04 14:29:27,877 fail2ban.actions: WARNING [dbmail] Ban 255.255.255.255

Что говорит о том, что атакующий забанен.

Как показывает практика, любую задачу можно решать разными способами. Изобретать велосипеды, конечно, занимательно, но намного дольше чем использовать готовые инструменты.

Категории: Linux PHP
Тэги: dbmailfail2ban