Как-то раз один знакомый сисадмин пожаловался мне на жизнь суровую. Он рассказал об одном инциденте в его конторе. Стоит оговориться, что контора небольшая и такой сущности как отдельный специалист по информационной безопасности там нет. Инцидент стандартный до банальности. Случайно заметили аномальную активность на линуксовых серверах. Подозрения сразу же подтвердились выводом команды who, который показал подключение по ssh с прокси-сервера с IP одной маленькой, но очень гордой страны. Дальше было то, что и положено в таких ситуациях, а именно: сменить доступы, понять откуда зараза по сети пошла, и что именно она делала. Доступы сменили, а вот когда полезли в логи, с удивлением обнаружил, что они уже несколько дней как пишутся в /dev/null, то есть у злоумышленника на сервере был root-доступ. Позже выяснили, что причиной была утечка пароля от аккаунта одного из сотрудников с доступом к sudo. История, в общем-то, типичная, тысячи таких. Но меня она зацепила и побудила задаться вопросом: а как, собственно поймать хакера в тот самый момент, когда он попал на сервер впервые и пытается там закрепиться? Возможно, существуют enterprise-решения аудита и мониторинга входа на удалённую машину, но даже крупный бизнес с неохотой тратится на инфобез. Не говоря уже о небольших конторах с IT-отделом в 3,5 человека. Будем делать всё сами, благо в линуксах требуемая функциональность есть практически из коробки. PAM используется во всех современных *nix-like системах. Арч-вики подсказывает нам, что PAM — это фреймворк для аутентификации пользователей в системе. PAM предоставляет возможность разрабатывать программы, которые не зависят от схемы аутентификации. Во время выполнения к программам подключаются модули аутентификации. Какой конкретно модуль аутентификации должен быть подключен, зависит от настроек локальной системы и остаётся на усмотрение локального системного администратора. Проще говоря, благодаря PAM в процесс аутентификации можно встроить дополнительные действия. Например поставить модуль для двухфакторной аутентификации, ну или в нашем случае добавить уведомление. PAM присутствует не только в Linux-системах, но и также в BSD и MacOS. Но раз уж говорим про Linux, на его примере и будем рассматривать. Настройки PAM можно найти в конфигах, лежащих в /etc/pam.conf и /etc/pam.d/. Туда мы и взглянем.  Конфигурационный файл представляет собой описание правил проверки, необходимых для успешной аутентификации пользователя или выполнения других операций. Строка в файле содержит одно правило, и проверки осуществляются последовательно. Каждая строка содержит три параметра: тип модуля, control_flag и имя модуля. Наибольший интерес для нас представляют модули типа "auth", поскольку именно они отвечают за аутентификацию. Control_flag определяет свойства модуля и может иметь следующие значения: requisite (необходимый) — если модуль возвращает положительный ответ, остальные проверки в цепочке выполняются, и запрос удовлетворяется. В случае отрицательного ответа запрос немедленно отвергается, и другие проверки не выполняются. required (требуемый) — аналогично requisite: при положительном ответе выполняются остальные проверки в цепочке. Отличие заключается в том, что при отрицательном ответе цепочка проверок продолжает работу, но запрос отклоняется. sufficient (достаточный) — удовлетворяет запрос, если ни одна из предыдущих проверок в цепочке не вернула отрицательный результат. В случае негативного срабатывания модуля результат игнорируется, и проверка продолжается. optional (необязательный) — модуль выполняет свою работу, но результат его работы не учитывается. Общие для всех сервисов правила записаны в common-auth, взглянем и туда тоже.  Что здесь происходит: отрабатывает модуль pam_unix.so, и в случае неуспешной отработки pam_unix.so выполняется pam_deny.so и следующие инструкции не выполняются. В случае успеха pam_unix.so следует  инструкция из следующей строчки, то есть pam_deny.so игнорируется, и выполняется pam_permit.so. Предполагаем следующую ситуацию. В один прекрасный момент злоумышленник получает рута, сервер становится полностью скомпрометированным. Верить логам с сервера мы больше не можем, хакер ими может манипулировать, причём хакер, который уже вышел из школьного возраста сделает это в первую очередь, потерев логи с предыдущими (успешными и не очень) попытками пройти аутентификацию. Итак, что из этого следует. Периметром атаки является полностью сервер, следовательно нам нужно отправлять данные о попытке логина куда-то вовне этого самого периметра. Для этого можно написать собственный PAM-модуль. А можно ограничиться простым скриптом, подключаемым через pam_exec.so. На гитхабе есть куча различных вариантов, с отправкой логов в Telegram, Slack, Discord, да куда угодно. Но если ничего не нравится, можно на коленке накидать самому за 5 минут. Необходимые значения pam_exeс записывает в переменные окружения. PAM_SERVICE — сервис, через который производится логин, например ssh, logind или lightdm. PAM_USER — логин пользователя, под которым осуществляется попытка входа. PAM_RHOST — IP машины, с которой происходит вход, в случае удалённого входа. Код скрипта с отправкой в Telegram: Редактируем /etc/pam.d/common-auth: Что мы здесь сделали: Меняем параметр success, устанавливая значение 2, то есть говорим: в случае успеха pam_unix.so игнорировать две следующих инструкции. И добавляем опциональную инструкцию, не влияющую на конечную проверку, с командой запустить наш скрипт с аргументом fail. Перед pam_permit.so добавляем опциональную инструкцию запуска скрипта с параметром success. В общем-то всё кажется просто, но когда серверов или пользователей становится много, логи превращаются в месиво. Вроде бы и видно, кто, когда и куда зашёл, но глаза замыливаются и в длинном списке сообщений о логине становится проблематично найти нужную запись или заметить аномалию. Что ж, будем превращать маленький безымянный скриптик в полноценную систему мониторинга и аудита попыток логина. Система получает имя Gryffine. Мы помним, что у нас есть необходимость вынести логи активности за пределы периметра потенциальной атаки. Если мы можем отправлять информацию в Telegram, значит мы можем её отправлять по API на свой сервер, который будет заниматься обработкой полученных данных. Модифицируем клиентскую часть для отправки информации в более понятном для машины варианте: Суть вся та же самая, за исключением пары моментов. Для более удобной автоматизации установки на множество машин выносим в аргументы принимающий эндпоинт и добавляем запись в syslog об ошибках. Раз мы поменяли аргументы, придётся ещё раз поправить common-auth. Теперь пишем бэкенд для нашего сервера. На эндпоинт приходит информация о попытке логина в формате: С этим уже можно работать. Для начала сделаем простенький веб-интерфейс для отображения этих попыток. Для этого берём Django, и оборачиваем приходящую информацию в объект записи, для последующего вывода в табличку. А дальше поехали навешивать плюшки. Если есть внешний IP-адрес, то можно определить к какой стране он принадлежит, для чего воспользуемся базой GeoLite2. Принадлежность IP к той или иной стране — это уже довольно ценная информация. Если серверами пользуется команда, расположенная в России, то внезапный успешный логин откуда-нибудь из Китая или ЮАР наводит на странные мысли. Если у нас есть возможность производить проверку по стране, почему бы некоторые страны, которые славятся обилием ботов не объявить подозрительными? Для этого сделаем блэк и вайтлисты для стран и диапазонов IP, причём не забудем о тех специалистах, кто мог релоцироваться. Можно добавить некоторую страну целиком в блэклист, а диапазоны, с которых может быть легитимный вход — в вайтлист, и наличие в вайтлисте будет приоритетнее. В итоге, модели будут выглядеть примерно так: Наконец, вспоминаем ради чего мы начали делать серверную часть. Django-tables2 даст нам удобное отображение, а django-filter удобный поиск и фильтрацию. Также прикручиваем возможность экспорта логов в csv и xls. В итоге получаем вот такую картинку. Цель каким-то образом блокировать подозрительные попытки входа не ставится. Для этого есть фаерволлы, fail2ban, короче говоря, специализированные для этого инструменты. Информация в таблице несёт по большей части справочный характер: появилась "красная" запись — значит нужно разобраться, что происходит, уточнить не забыл ли случайно сотрудних перед входом на рабочий сервер отключить VPN с которого смотрел Netflix, или это всё-таки хакеры шалят. Много серых — значит плохо настроен фаерволл, боты брутят ssh. Если начал навешивать плюшки, становится трудно остановиться. Вернём оповещения о логине в Telegram, только теперь этим будет заниматься сервер. При создании экземпляра записи лога опционально отправляем информацию об этом событии в Telegram, а заодно и на e-mail. Также важно понимать, что подобный лог уже сам по себе представляет большую ценность потенциальному злоумышленнику. Хостнеймы дают понимание о топологии сети, а логины юзеров помогут при составлении списков для брута. Поэтому даже для чтения вешаем авторизацию. Заодно в профиле юзера можно повесить telegram id и почту, куда логи дублировать. Или не вешать, это опционально. Разумным решением будет не выпускать ресурс с логами в интернет, оставив доступ исключительно из корпоративной сети. Наконец, заворачиваем всё, что у нас получилось в Docker и прикручиваем обновление базы GeoIP при каждом перезапуске контейнера. Итог вышел, по моему мнению, довольно неплохим. Система позволяет оперативно обнаруживать аномальную активность и реагировать на неё. Успешно обнаруженная зараза — это уже наполовину обезвреженная зараза. Проект опубликован на гитхабе, серверная часть, клиентская часть.  Если вам нужен бэкендер, который понимает в линуксах, буду рад пообщаться. Ссылка на резюме, почта для связи pressxtowin7@gmail.com.  Python-разработчик