Защита unix сервера от подбора паролей

Ранее, я уже рассказывал о защите Unix сервера от подбора паролей ( bruteforce ). Речь шла об SSH и программе sshguard, фиксирующей неудачные попытки входа в систему по данному протоколу. Здесь хотелось-бы рассказать о более комплексном решении, для защиты практически любого сервиса - программе Fail2ban.

Собственно fail2ban, это небольшой набор скриптов, написанных на python. Принцип работы прост, отслеживать изменения в лог файлах различных сервисов и по определенным сигнатурам, выполнять определенные действия. Для поиска характерных признаков подбора пароля, используются регулярные выражения в файлах фильтров. Для выполнения действий используются action файлы.

На Linux с настройкой данной программы вообще никаких трудностей не возникает, на сервере FreeBSD всплыли некоторые специфические моменты.

Итак, операционная система FreeBSD 8.2 amd64, настраивать будем защиту sshd и фтп сервера proftpd. Для работы естественно потребуется включенный фаервол, во FreeBSD штатным является ipfw.

Установка fail2ban

freebsd82 /# cd /usr/ports/security/py-fail2ban
freebsd82 /usr/ports/security/py-fail2ban# make install clean

После установки, которая длится меньше минуты, идем по адресу /usr/local/etc/fail2ban, наблюдаем следующую структуру каталога:

  • папка action.d - содержит файлы действий
  • папка filter.d - файлы фильтров
  • файл fail2ban.conf - основной файл конфигурации
  • файл jail.conf - файл настройки защиты конкретных сервисов

Настройка fail2ban

В первую очередь копируем файл jail.conf в файл с именем jail.local, что-бы файл по умолчанию у нас оставался нетронутым, все настройки лучше делать в jail.local.

freebsd82 /# cp /usr/local/etc/fail2ban/jail.conf /usr/local/etc/fail2ban/jail.local

Для начала заглянем в основной файл конфигурации:

[Definition]

loglevel = 4 # На время настройки я ставлю 4, максимально подробный уровень логгирования
logtarget = /var/log/fail2ban/fail2ban.log # лог файл fail2ban, имейте в виду, если вы указываете свое расположение файла логов, как я в данном случае, нужно что-бы директория, где он будет расположен, была доступна на запись только пользователю root, иначе fail2ban не запустится.
socket = /var/run/fail2ban/fail2ban.sock # файл сокета для связи клиентской и серверной части программы, оставляем по умолчанию

Тут я изменил только расположение файла логов, как мне удобней и поднял уровень логгирования на время отладки.

Далее настроим взаимодействие с фаерволом ipfw. Идем в папку action.d, как видите фаерволы тут представлены в ассортименте, поскольку мы используем FreeBSD с ipfw, нас интересует файл bsd-ipfw.conf, вообще можно создать и свой с произвольным именем, главное не забыть это учесть при дальнейшей настройке в jail.local. По аналогии с файлом jail.conf, копируем его с именем bsd-ipfw.local.

Здесь можно оставить по умолчанию но я предпочитаю дописать в actionstop, команду для обнуления таблицы с забаненными IP адресами, при выключении fail2ban. Если сравнить этот файл, например с iptables.conf ( для стандартного фаервола в linux дистрибутивах — iptables ), можно заметить некоторые отличия, поскольку данные фаерволы устроены по разному и принципы организации правил фильтрации у них тоже разные.

Файл после моей правки выглядит так:

[Definition]
actionstart = # команда выполняемая при старте fail2ban
actionstop = pfw table 1 flush # команда выполняемая при остановке, обнуляем блокирующую таблицу
actioncheck = # команда проверки
actionban = ipfw table 1 add  # забанить IP адрес, при срабатывании, IP добавляется в таблицу 1 ipfw
actionunban = ipfw table 1 delete  # обратное действие, удалить IP из таблицы

все остальное я закомментировал

Что-бы данный вариант работал, нужно руками добавить в ipfw правило, блокирующее все IP адреса, находящиеся в таблице table 1:

freebsd82 /root# ipfw add 100 deny all from table\(1\) to me # экранируем скобки и не ставим пробелов
freebsd82 /root# ipfw show
00100     0        0 deny ip from table(1) to me # правило которые мы только что добавили
65535 65255 15896158 allow ip from any to any

Есть еще один вариант, можно поступить например так:

в actionstart прописать команду автоматического создания правила:

actionstart = ipfw add 100 deny all from table\(1\) to me

а в actionstop, вдобавок к обнулению таблицы, дописать еще и команду удаления, например так:

actionstop = ipfw -q delete `ipfw -q show | grep "table(1)" | awk '{ print $1;}'`
	      ipfw table 1 flush

обе команды сработают по очереди. Но у ipfw есть такая особенность, в него можно добавлять кучу одинаковых правил с одним и тем-же номером, без каких-либо сообщений об ошибке. Срабатывать при этом будет только первое из них.

Получится следующая ситуация, мы прописали команду в actionstart, как я привел парой строк выше, а мониторить собираемся например 3 сервиса, sshd, proftpd и dovecot, значит при инициализации fail2ban, файл bsd-ipfw.local, будет прочитан 3 раза, соответственно команда:

ipfw add 100 deny all from table\(1\) to me

тоже будет выполнена трижды, и соответственно у нас в фаерволе появится 3 одинаковых правила, и 2 из них не будут выполнять никаких функций, лишь мозолить глаза и сбивать с толку:

freebsd82 /root# ipfw show
00100     9      522 deny ip from table(1) to me # 1 работающее правило
00100     0        0 deny ip from table(1) to me # 2
00100     0        0 deny ip from table(1) to me # 3
65535 67537 16429215 allow ip from any to any

если вас это не смущает, можете сделать так, никаких негативных моментов это не несет, не считая конечно некоторой путаницы с правилами.

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

Далее перейдем в каталог filter.d. Тут у нас лежат фильтры, которые по регулярному выражению ищут определенные строки в лог файлах. Нас интересуют sshd.conf и proftpd.conf, как и ранее копируем их в sshd.local и proftpd.local соответственно.

В файле sshd.local, я все оставил как есть. В proftpd.local пришлось внести некоторые незначительные изменения. Вот содержимое измененного файла:

[Definition]
failregex = \(\S+\[<HOST>\]\)[: -]+ USER \S+: no such user found from \S+ \[\S+\] to \S+:\S+.*$
            \(\S+\[<HOST>\]\)[: -]+ USER \S+ \(Login failed\): Incorrect password\..*$
            \(\S+\[<HOST>\]\)[: -]+ SECURITY VIOLATION: \S+ login attempted\..*$
            \(\S+\[<HOST>\]\)[: -]+ Maximum login attempts \(\d+\) exceeded.*$

ignoreregex = # Сюда можно вписать регулярные выражения строк, которые нужно игнорировать

Дело в том, что регулярные выражения прописанные тут по умолчанию, ничего не находили в лог файле. Проверить работоспособность того или иного фильтра с регулярными выражениями можно с помощью скрипта fail2ban-regex, идущего в поставке fail2ban. Например проверяем фильтр proftpd.local, натравливая его на лог файл авторизаций:

freebsd82 /root# fail2ban-regex /var/log/auth.log /usr/local/etc/fail2ban/filter.d/proftpd.local

Данная команда выведет полную статистику, сколько строк из лог файла попадают под то или иное регулярное выражение, в отладке очень полезная штука.

Я никогда не сталкивался с python, тем более с его регулярными выражениями, хотя они и похожи на Perl, тем не менее различия есть. Попробовав разные вариант, дописал в конец каждого выражения ".*", после чего прогнал проверку через fail2ban-regex, все что нужно, находится.

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

[DEFAULT]

[ssh-ipfw]
enabled  = true # включить мониторинг
ignoreip = 127.0.0.1 192.168.50.200 # IP адреса которые нужно игнорировать
filter   = sshd # какой файл фильтра использовать, то есть по сути это название файла в папке filter.d, только без расширения
action   = bsd-ipfw # какой действие использовать, название аналогично фильтру
logpath  = /var/log/auth.log # анализируемый лог файл
bantime  = 60 # продолжительность блокировки, подбираем на свое усмотрение
maxretry = 2 # количество неудачных авторизаций с IP, значение подбирается по ситуации/нагрузке на сервис в рамках данной машины
findtime  = 600 # время, в течении которого нужно фиксировать maxretry, то есть у нас получается 2 ошибки в течении 10 минут
backend = poller # способ получения информации о модификации лог файла, можно поставить auto или gamin


[proftpd-ipfw] # тут все аналогично
enabled  = true
filter   = proftpd
action   = bsd-ipfw
logpath  = /var/log/auth.log
ignoreip = 127.0.0.1 192.168.50.200
bantime  = 60
maxretry = 2
findtime  = 600
backend = poller

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

action = bsd-ipfw
                  mail-whois[name=ProFTPD, dest=admin@domain.com]

и отредактировать соответствующий файл в директории action.d, в данном случае mail-whois.local, там все довольно просто:

[Definition]
actionstart =
actionstop =
actioncheck =
actionban = printf %%b "Hi,\n
            The IP  has just been banned by Fail2Ban after
             attempts against .\n\n
            Here are more information about :\n
            `whois `\n
            Regards,\n
            Fail2Ban"|mail -s "[Fail2Ban] : banned " 

actionunban =
[Init]
name = default
dest = admin@domain.com

На этом с настройками вроде закончили, можно запускать и проверять.

Запуск и проверка fail2ban

Мне удобней просматривать лог в режиме realtime, в отдельном окне консоли или в отдельном окне, оконного менеджера для терминала — screen, делаем:

freebsd82 /# tail -f /var/log/fail2ban/fail2ban.log

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

Если запуск прошел успешно, проверяем блокировку. Я поступаю просто, со стороннего сервера запускаю:

debian:/# ssh 192.168.50.200

и жму на ентер, или ввожу значения от балды и жму на ентер), наблюдая за логом fail2ban, при loglevel = 4, там буду фиксироваться все изменения наблюдаемого файла, тек-же найденные IP адреса и действия производимые с ними.

В течении нескольких секунд после достижения порога неудачных логинов к ssh, IP адрес блокируется путем добавления его в таблицу table 1 фаервола, по истечении времени блокировки, адрес будет автоматически удален. Аналогично проверяется и ftp.

На Linux все настраивается аналогично, разве что может чуть попроще, с использованием соответствующих фаерволу, action файлов.

Вроде ничего не упустил.

Комментарии

Здравствуйте, хорошая статья, не подскажете как в случае PF все это делается?

сам не пробовал, но для PF там тоже action файл есть, думаю подкорректировать не проблема

у вас ошибка в конфиге асtion'a там вы применяете то PF то IPFW....

У меня вот ваши правила для proftpd не сработали, пришлось с помощью отладчика сделать свои:

failregex = \(.+\[::.+:\]\).+ USER \S+: no such user found from .+\[.+\] to .+$
\(.+\[::.+:\]\).+ USER .+ \(Login failed\): Incorrect password\. $
\(.+\[::.+:\]\).+ SECURITY VIOLATION: \S+ login attempted\. $
\(.+\[::.+:\]\).+ Maximum login attempts \(\d+\) exceeded $

Логи такого типа:
Jul 29 09:42:52 host proftpd[10175]: *** (::ffff:***.249.0.40[::ffff:46.249.0.40]) - USER admin: no such user found from ::ffff:46.249.0.40 [::ffff:***.249.0.40] to ::ffff:***.162.182.163:21
Jul 29 09:42:57 host proftpd[10180]: *** (::ffff:***.249.0.40[::ffff:46.249.0.40]) - SECURITY VIOLATION: root login attempted.
Jul 29 09:43:09 host proftpd[10189]: *** (::ffff:***.249.0.40[::ffff:46.249.0.40]) - USER advi-user (Login failed): Incorrect password.

Может кому пригодистся )

спасибо, честно говоря так и не понял почуму у меня дэфолтовые регекспы не матчились)
на фряхе делали, или линукс?

на фряхе, есть веб сервер созданный при помощи чистой фряхи и isp manager. Вот и решил что надо защититься от перебора паролей по ssh и фтп. Вот думаю что неплохо было бы попробовать прикрутить fail2ban к авторизации isp manager.

хорошая идея

даже правильнее будет вот так:

failregex = \(.+\[::.+:<HOST>\]\).+ USER \S+: no such user found from .+\[.+\] to .+$
\(.+\[::.+:<HOST>\]\).+ USER .+ \(Login failed\): Incorrect password\. $
\(.+\[::.+:<HOST>\]\).+ SECURITY VIOLATION: \S+ login attempted\. $
\(.+\[::.+:<HOST>\]\).+ Maximum login attempts \(\d+\) exceeded $

fail2ban без записи <HOST> - неработает

о как обратил внимание в коментах режется запись "<"HOST">", её надо вводить без кавычек в failregex = \(.+\[::.+:"<"HOST">"\]\)

да, непорядок, поправлю )
спасибо

Ребят, прошу помощи!

Проблема заключается в следующем:
при исполнении fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/profttd.conf на экран выводится:

...
No 'host' group in '/etc/fail2ban/filter.d/profttd.conf'
Cannot remove regular expression. Index 0 is not valid
No 'host' group in '/etc/fail2ban/filter.d/profttd.conf'
Cannot remove regular expression. Index 0 is not valid

Results
=======

Failregex
|- Regular expressions:
| [1] /etc/fail2ban/filter.d/profttd.conf
|
`- Number of matches:
[1] 0 match(es)

Ignoreregex
|- Regular expressions:
|
`- Number of matches:

Summary
=======

Sorry, no match

Look at the above section 'Running tests' which could contain important
information.

Вырезка из proftpd.conf:
....
failregex = \(.+\[::.+:\]\).+ USER \S+: no such user found from .+\[.+\] to .+$
\(.+\[::.+:\]\).+ USER .+ \(Login failed\): Incorrect password\. $
\(.+\[::.+:\]\).+ SECURITY VIOLATION: \S+ login attempted\. $
\(.+\[::.+:\]\).+ Maximum login attempts \(\d+\) exceeded $
...

Заранее благодарен

ты чем в коменты смотрел ? ты видел что я писал что здесь форма отправки коментов зарезала запись HOST ?

поправил <HOST> в тексте статьи
все руки не дойдут вылечить этот фильтр

Спасибо!

Настроил. По простому. Только не понял один ньюанс:
findtime = 600 Это время через которое утилита проверяет лог например постфикса?
Или она проверяет чаще. Если поставлю: findtime = 1800
то тогда злодей успеет напихать мне попыток очень много. за 30 минут
а fail2ban проверив этот отрезок и сравнив его с maxretry = 2
запоздало всетаки забанит вредителя. Может я заблуждаюсь.

немного не так, это время в течении которого учитываются неудачные попытки, если буквально, "искать 2 неудачных попытки логина, произошедшие в ПОСЛЕДНИЕ 600 секунд", само по себе, текущее время никакой роли не играет

ну и естественно вы можете выставить свои временнЫе интервалы и кол-во попыток.. исходя из своей практики и уровня паранойи :)

Ну вроде успокоили
Спасибо.
А то было сомнение. Значит fail2ban работает как бы в режиме tail -f loggmail
Не цепляя просто временные отрезки из лога (заданые findtime = )
Если 2 попытки произойдут в первые 10 сек (при findtime = 1800) то бан сработает сразу.
Я эту утилиту приспособил для постфикса. Бомбят знаете ли по авторизации sasl и еще
с ботов сыпется спам. Обработка писем занимает дольше времени чем например
авторизация, поэтому пришлось увеличить findtime =.

все верно, грубо оворя, софтина парсит последнее изменение логфайла +анализирует времееннЫе метки в самОм логе, на основнии этого делает выводы, и если что, тут-же добавляет правило на фаер.. этого вполне достаточно, что-бы остановить подбор пароля, при желании конечно можно поставить и более длинный бан, но смысла нет, потому что IP постоянно меняются

Понятно. Тогда последний вопрос. Если Вам не трудно.

Нужно ли в jail.conf отключать ## настройки по умолчанию?
Понаблюдав за логом утилиты при запуске пишет- параметры которые устанавливаются с секций конфа, потом дефолтные
устанавливаются. То есть как бы перекрываются. В логе не указывается к каким фильтрам они применяются.

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
Регистр имеет значение
              ooooooooo.        o8o  ooooo      ooo   .ooooo.                
`888 `Y88. `"' `888b. `8' 888' `Y88.
ooo. .oo. 888 .d88' oooo 8 `88b. 8 888 888 oooo oooo
`888P"Y88b 888ooo88P' `888 8 `88b. 8 `Vbood888 `888 `888
888 888 888`88b. 888 8 `88b.8 888' 888 888
888 888 888 `88b. 888 8 `888 .88P' 888 888
o888o o888o o888o o888o 888 o8o `8 .oP' `V88V"V8P'
888
.o. 88P
`Y888P
Введите код, изображенный в стиле ASCII-арт.