Всех категорически приветствую!
Сегодня разберем некоторые моменты, связанные с защитой системы (не только серверной) от несанкционированного доступа (преимущественно извне).
В прошлых статьях мы настроили web-сервер и организовали в нем базовую защиту каталогов средствами .htaccess и .htpasswd, и попасть, скажем так, на страницу сайта теперь можно только введя логин и пароль. Но есть один нюанс: вводить эти логин с паролем можно до бесконечности, пока не угадаешь правильную пару. Это хорошая дыра в безопасности для «ломоботов», жаждущих посмотреть на то, что же там у нас имеется за заветной формой авторизации. Как происходит брутфорс и чем это чревато, думаю, разъяснять не нужно.
В этом случае к нам на помощь придет относительно простая, но в то же время, эффективная и популярная софтинка – fail2ban (или покороче – f2b).
Если откинуть все частности и обвесы, то принцип работы f2b достаточно прост:
- скрипт f2b «читает» логи той программы, которая находится «под его защитой»;
- сравнивает сообщение в логе с шаблоном, заложенным в конфигурационном файле (фильтре) f2b;
- если сообщение совпадает с шаблонным, то включает счетчик попыток;
- если попытки исчерпаны – блокирует доступ на порт с того IP адреса, с которого, собственно, приходят подозрительные запросы.
Как можно догадаться, зная ключевые сообщения лога той или иной программы, fail2ban можно прикрутить к любому софту, у которого ведется более-менее адекватный лог.
Такое «прикручивание» сводится к созданию кастомного фильтра и набора правил, которые будут этот фильтр обрабатывать. Именно создание кастомной конфигурации мы и разберем в статье.
В базовом пакете fail2ban уже имеются конфигурации для большинства программ, подверженных сетевым атакам: SSH, Apache, всякие почтовые серверы, Asterisk, vsftpd (FTP-сервер) и множество других. Поэтому прежде чем начать изобретать свой велосипед, посмотрите на конфиги в каталоге /etc/fail2ban/filter.d – вполне возможно, что фильтр для софта, который вы хотите защитить, там уже есть и останется только грамотно его донастроить и включить.
Разжевывать настройку каждого фильтра для популярных служб смысла нет – их много, да и в гугле адекватной информации с избытком. Почитать, например, о настройке фильтра:
Sshd – ТУТ
Vsftpd - ТУТ
Защиту f2b для Apache мы включим в конце статьи. А сейчас немного сменим курс.
Кастомный фильтр для openvpn
Я рассмотрю создание кастома на примере openvpn сервера. Для понимания общего принципа более чем достаточно и не сильно сложно. Сам openvpn сервер мы развернем в следующей статье (да, у меня он уже развернут, но подготовить защиту можно и без его наличия).
К сожалению, в базовой конфигурации f2b, на время написания статьи, я не нашел фильтра для openvpn. Поскольку защита openvpn сервера для меня является актуальной темой, пришлось быстро изобретать свой довесок, чтобы было чем «надавать по рукам» вредителям, безудержно хотящим присосаться к VPN с левыми сертификатами и перебором пар логин/пароль.
Да, в инете есть готовые f2b фильтры для openvpn сервера, можно позаимствовать, но главное – понимать, что там написано и как оно будет работать.
Для того, чтобы такой фильтр изобрести, нужно пошарить и найти в интернете записи из логов openvpn сервера, которые указывают на неудачные попытки аутентификации, либо же самому на время стать вредителем для своего сервера.
Первое, что стоит сделать – настроить логирование openvpn сервера, чтобы fail2ban было откуда читать информацию. Для этого надо сходить в конфигурацию сервера /etc/openvpn/server.conf и раскомментить строчку с логом:
Затем перезапустить openvpn сервер командой:
#> systemctl restart openvpn@server
По умолчанию, openvpn пишет логи в syslog. Читать можно и из него, но не советую использовать такой подход, поскольку в syslog пишется куча всякой другой информации от других служб, и, помимо того, что это приведет к повышенной нагрузке не сервис f2b (а, как следствие, на ресурсы железа), так еще и фильтры будут срабатывать ошибочно, если сообщения от одних служб будут схожи с другими. Чтобы f2b работал более избирательно и продуктивно, желательно (там, где возможно) использовать «индивидуальные» логи.
Теперь надо понять (почитать, погуглить, поэкспериментировать), какие записи будет оставлять в логе openvpn сервер в случае ошибок аутентификации на сервере. Наиболее частые случаи такие:
- неверный сертификат (не та подпись, истекший срок действия);
- неверная пара логин/пароль;
- неверный ключ TLS аутентификации ta.key;
- несоответствие шифрования.
В большинстве случаев, если параметры аутентификации неверные, сервер сбрасывает соединение, фиксируя в логе запись «Connection reset, restarting».
Если сертификат клиента не проходит верификацию, то в логе будет запись типа «VERIFY ERROR». Если проблемы с ta.key, с шифрованием или с обменом ключами, то будет запись «TLS Error: TLS handshake failed» или «TLS Auth Error». При этом в этих же строках будет указан IP адрес и порт инициатора запроса. Вот один из примеров сброса соединения с клиентом:
Сброс так же может происходить и по причинам, которые не связанны с аутентификацией, например: если есть проблемы со связью, режется протокол, не передаются сертификаты и т.п.. Но безопасность, как и красота, требует жертв – проще будет потом разобраться, почему сбрасывается соединение и произвести корректировку конфигурации клиента или сервера, нежели дать возможность невесть кому покошмарить VPN.
Поэтому запись о сбросе соединения в том числе будет занесена в «черный список» фильтра.
Особо вникать в подробности каждой записи лога не буду, приведу лишь список «провалов», который частично нашел в интернете (на том же сайте fail2ban.org), проанализировал, и частично дополнил из своего лога:
Mon Jun 8 10:38:54 byaka/85.140.23.244:12345 VERIFY ERROR: depth=1, error=self …
Mon Jun 8 10:38:54 172.16.0.46:40089 Connection reset, restarting [0]
Mon Jun 8 10:38:54 172.16.0.13:50001 TLS Auth Error
Mon Jun 8 10:38:54 172.16.0.23:48904 SIGUSR1[soft,ping-restart] received, process restarting
Mon Jun 8 10:38:54 172.16.0.27:31097 TLS Error: incoming packet authentication failed from [AF_INET] 172.16.0.27:31097
Mon Jun 8 10:38:54 172.16.0.23:31097 TLS Error: TLS handshake failed
Безусловно, в процессе эксплуатации список может потребовать корректировки и дополнения для более тонкой работы, но пока мне достаточно этого.
Теперь, основываясь на этих «провальных» записях, нарисуем фильтр для f2b.
Для начала установим сам fail2ban:
#> apt update
#> apt install fail2ban
Теперь создадим в директории фильтров f2b новый конфигурационный файл и выставим ему права:
#> touch /etc/fail2ban/filter.d/openvpn.conf
#> chown root:root /etc/fail2ban/filter.d/openvpn.conf
#> chmod 644 /etc/fail2ban/filter.d/openvpn.conf
Открываем его на редактирование и приводим к такому виду:
Скопировать код можно ТУТ
Теперь осознаем, что тут к чему.
По своей сути, файл фильтра - это набор шаблонов регулярных выражений.
Поскольку fail2ban написан на python, то правила описания регулярок диктует тоже python. Если хотите узнать про них подробнее – смотрите документацию на python, например, ТУТ.
Поскольку python активно развивается, нельзя исключать, что в более новых версиях правила описания регулярок и спецсимволы могут быть изменены, а это напрямую повлияет на корректность работы фильтра. Конечно, это не происходит от версии в версии, но… мало ли. Поэтому этот момент нужно учитывать и отслеживать.
Для того, чтобы узнать, с какой версией совместим ваш f2b, наберите команду:
#> apt-cache showpkg fail2ban
Будет выведен список зависимостей, в котором можно узнать, какую версию питона использует текущая версия f2b. У меня, например, python3:
Итак, разберем наш фильтр.
[definition] – определяем раздел с правилами фильтра;
failregex = – задаем массив (перечень) шаблонов регулярных выражений, по наличию совпадений с которыми будем банить клиента (своего рода, «черный список»);
Ignoreregex = – такой же массив, но работающий как «белый список».
«^» - каретка. Определяет начало строки (откуда начинать читать строку из лога);
<HOST> - переменная, в которую будет записан IP адрес или имя хоста;
«:» - просто двоеточие, отделяющее IP от порта;
«\d+» -какие-то цифры (любые) после двоеточия (но только до пробела);
«$» - обозначает конец строки, т.е. дальше читать строку из лога не нужно;
«\» - говорит о том, что после него будет строковый символ. Стоит перед квадратными скобками. Почему? Потому что если не поставить обратный слеш перед скобкой, то эту скобку интерпретатор python распознает не как часть строки, а как свой спецсимвол.
Теперь возьмем самую заковыристую строку, поставим ее рядом с правилом регулярки и прочитаем глазами python:
Mon Jun 8 10:38:54 172.16.0.23:48904 SIGUSR1[soft,ping-restart] received, process restarting
^ <HOST>:\d+ SIGUSR1\[soft,ping-restart\] received, process restarting
На языке python это звучит примерно так (утрированно, естественно):
- Осознаю, что все, что левее «^», не будет меня интересовать от слова «совсем»;
- Смотрю строку с начала и ищу «кусок», в котором есть двоеточие, пробелы по краям, а следом идет слово «SIGUSR1…» и чего-то там еще;
- Не нашел «кусок» - пропускаю всю строку целиком;
- Нашел «кусок» – пока отложу его в сторону. Если дальше все совпадет, то вернусь к нему;
- После «куска» в строке должен идти пробел и текст «SIGUSR1» - если он есть – читаю строку дальше, если нет – пропускаю всю строку целиком;
- Уперся в обратный слеш – значит следующий символ читаю как строку;
- Дальше в строке должно быть написано «[soft,ping-restart» - если так, то читаю дальше, если нет – то пропускаю всю строку целиком;
- Дальше опять обратный слеш – значит символ за ним читаю как строку;
- Дальше в строке должно быть написано «] received, process restarting» - если так, то читаю дальше, если нет – пропускаю всю строку целиком;
- Строка кончилась. Если выше не возникло причины пропустить всю строку, то возвращаюсь к «куску». Иначе - выкидываю «кусок» и читаю следующую строку;
- Если все-таки вернулся к «куску», то значение слева от двоеточия (от пробела до двоеточия) пишу в переменную HOST (в нашем случае - IP);
- Справа, от двоеточия до пробела, должны стоять какие-то цифры – просто игнорирую их.
Надеюсь, логика регулярки стала более понятной.
Таким образом, если строка из лога соответствует описанию регулярки, то f2b включает счетчик попыток для зафиксированного в переменной HOST IP-адреса.
***
Теперь нужно описать правила, которые будут этот фильтр, так скажем, обслуживать.
Эти правила содержатся в файле /etc/fail2ban/jail.conf
НО!
Данный файл рекомендуется скопировать в эту же директорию, но сменив расширение c «.conf», на «.local». Т.е. мы должны дополнительно заиметь файлик «jail.local» и править будем уже его.
Почему так? Потому что файлик «jail.conf» может подложить нам свинью с круглыми глазами. Круглые глаза у нас станут тогда, когда мы обновим f2b или внесем изменения через какой-нибудь сторонний интерфейс управления, а после не обнаружим в файле того, что мы так долго отстраивали и отлаживали.
f2b при обновлении может занулить всю конфигурацию в jail.conf под новую версию, а сторонние интерфейсы управления, если не озадачиваться механизмом их работы, могут перерисовать этот файлик под тот набор правил, который хранится в их собственной базе данных. И будет «ой…».
Смысл файлика «jail.local» в том, что он подгружается после основной конфигурации и переписывает правила, заданные в файлике «jail.conf», и в тоже время обычно остается изолированным от воздействия «внешних факторов».
Поэтому, заводим файлик «jail.local»:
#> cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Открываем его на редактирование, спускаемся в самый низ, и дописываем следующее:
Скопировать блок можно ТУТ.
Разберемся, что есть что.
[openvpn] – название раздела с правилами (называйте так, чтобы самим было понятно, к чему относится правило);
Port = – номер порта, на котором болтается openvpn сервер (кстати, не рекомендую использовать дефолтный порт 1194). Указывать нужно для того, чтобы f2b понимал, какой порт блокировать для запросов с «шаловливого» IP адреса;
protocol = – название протокола, по которому принимаются соединения. Обычно указывается в параметре «action». Если такого параметра нет, то указать надо здесь, иначе f2b закроет порт без указания протокола, т.е. наглухо.
logpath = – путь к файлу лога openvpn сервера, из которого будем читать строки;
bantime = – время, на которое будет заблокирован порт для «вредителя». У меня указано в секундах. В примере стоит бан на неделю. В нынешних версиях f2b можно так же указывать в часах (= 1h), в днях (= 2d), в неделях (= 3w);
enabled = – принимает значение «true» или «false». Говорит о том, будет ли правило включено или выключено;
filter = – указывает имя файла фильтра без расширения «.conf», который мы изобрели выше;
maxretry = – счетчик попыток для «вредителя». Попытки кончились – «вредитель» забанен;
ignoreip = – IP адреса «вредителей», которых не надо банить несмотря на хулиганство, например, свои IP адреса.
Сохраняем файлик.
Перезапускаем fail2ban:
#> systemctl restart fail2ban
Смотрим статус нашего правила:
#> fail2ban-client status openvpn
Как видно, пока все чисто: нет ни одной провальной попытки и ни одного забаненного «вредителя».
Теперь немножко нахулиганим и посмотрим статус еще раз:
Видим, что на шестом провале был уличен и забанен «хулиган» с IP 85.140.23.244. Повторно он сможет похулиганить только через неделю.
(адрес МТС со свистка, динамика, так что можете на него не стучаться)
Но, вдруг выяснилось, что это знакомый «вредитель», выставивший в конфигах не те сертификаты. Звоним, высказываем ему свое «фи», помогаем правильно настроить openvpn клиент и… ну, не неделю ж ему теперь ждать – так и быть, разбаним:
#> fail2ban-client set openvpn unbanip 85.140.23.244
IP удалится списка забаненных и порт снова станет для него доступен.
На этом с кастомным фильтром все.
Теперь вернемся к Apache.
Если кто не читал, то в предыдущей статье мы ограничили доступ к директории админки сайта "test-site1.ru/adminka" средствами .htaccess и .htpasswd, и теперь Apache у нас просит пароль. Но, как я говорил в начале статьи, пока это лишь поле для тренировок «вредителей».
Нужно ограничить количество попыток неверной аутентификации и, на всякий случай, забанить «подозрительного типа» на некоторое время, чтобы неповадно было.
Для того, чтобы заставить fail2ban следить за аутентификацией в apache, нужно сделать следующее:
1) В конфигурации требуемого хоста (в нашем случае test-site1.ru) нужно включить логирование, и файл лога назвать так, чтобы в конце присутствовало «error.log» (на такой шаблон имени «провальных» логов apache заточен f2b).
Строка в конфиге вашего хоста в Apache должна будет выглядеть примерно так:
После того, как поправили конфиг хоста, перезапустим Apache:
#> systemctl restart apache2
И сходим проверим, появился ли наш файлик с логом в папке /var/log/apache2
2) Скопировать файлик «/etc/fail2ban/paths-common.conf» в эту же директорию, но с именем «paths-override.conf» (суть такая же, как и у jail.local)
#> cp /etc/fail2ban/paths-common.conf /etc/fail2ban/paths-override.conf
3) Отредактировать /etc/fail2ban/jail.local, включив в нем секцию «[apache-auth]» и задав параметры для бана:
4) Перезапустить f2b:
#> systemctl restart fail2ban
Теперь попробуем похулиганить в форме авторизации админки, и в итоге получим вот такую картину:
Ну, а как разбанивать «врелителей» вы уже знаете.
Конечно, для Apache в f2b есть и другие фильтры, отвечающие, например, за обращение к несуществующему контенту и т.п., но про них уже не в этот раз.
На этом сегодня все!
Шпаргалка:
Посмотреть статус работы правила f2b: fail2ban-client status имя_правила
Разбанить IP несчастного: fail2ban-client set имя_правила unbanip IP_адрес
В следующей статье установим и настроим openvpn сервер. Разберем некоторые нюансы.
Ставьте лайк и подписывайтесь!
До связи!