RFC (Request for Comments, Запрос на комментарии) - серия документов, публикуемая сообществом исследователей и разработчиков, руководствующихся практическими интересами, в которой описывается набор протоколов и обобщается опыт функционирования Интернет.
Ранее, я уже рассказывал о защите 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.
[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, вдобавок к обнулению таблицы, дописать еще и команду удаления, например так:
обе команды сработают по очереди. Но у 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, натравливая его на лог файл авторизаций:
Данная команда выведет полную статистику, сколько строк из лог файла
попадают под то или иное регулярное выражение, в отладке очень полезная
штука.
Я никогда не сталкивался с 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.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
На этом с настройками вроде закончили, можно запускать и проверять.
теперь в этом окне у нас будут вылезать на экран все строки попадающие в лог, по мере их поступления.
Если запуск прошел успешно, проверяем блокировку. Я поступаю просто, со стороннего сервера запускаю:
debian:/# ssh 192.168.50.200
и жму на ентер, или ввожу значения от балды и жму на ентер), наблюдая за логом fail2ban, при loglevel = 4, там буду фиксироваться все изменения наблюдаемого файла, тек-же найденные IP адреса и действия производимые с ними.
В течении нескольких секунд после достижения порога неудачных логинов к ssh, IP адрес блокируется путем добавления его в таблицу table 1 фаервола, по истечении времени блокировки, адрес будет автоматически удален. Аналогично проверяется и ftp.
На Linux все настраивается аналогично, разве что может чуть попроще, с использованием соответствующих фаерволу, action файлов.