Ранее, я уже рассказывал о защите 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 файлов.
Вроде ничего не упустил.