Логи не пишут, кто что сделал: настраиваем ответственность заранее
Сервер лёг, в базе пропали строки, кто-то отключил файрвол — и первый вопрос на разборе инцидента звучит просто: «кто это сделал?» А ответ в логах один и тот же для всей команды: root. Если пять человек заходят по общему паролю или общему SSH-ключу и работают от одной учётной записи, auth.log и bash_history знают только то, что кто-то что-то сделал под root — не кто именно. Это не гипотетическая проблема на будущее, это дыра, которую обнаруживают именно в момент, когда она больнее всего — во время расследования. Разберём, как настроить персональный доступ и аудит заранее, пока это спокойная задача на час, а не паника посреди инцидента.
Содержание
- Почему общий root — это ловушка, которая срабатывает только один раз
- Именные учётные записи вместо общего root
- sudo с логированием: кто, что и когда
- auditd: аудит на уровне ядра, а не только оболочки
- Привязка истории команд к конкретному пользователю
- Централизованный сбор логов: чтобы их нельзя было стереть локально
- Почему это работает только на счёт «до», а не «после»
Почему общий root — это ловушка, которая срабатывает только один раз
На старте небольшого проекта общий root кажется рациональным решением: один VPS, два-три человека, все свои, ключ лежит в общем менеджере паролей — удобно и быстро. Дальше проект растёт, команда меняется, кто-то уходит, кто-то подключает подрядчика на пару задач — а модель доступа остаётся той же: один пароль или один ключ на всех, sudo su - при входе, дальше делай что хочешь под root.
Проблема не в том, что это неудобно в повседневной работе — как раз наоборот, поначалу всё работает гладко. Проблема вскрывается в трёх конкретных ситуациях:
- Инцидент безопасности. Кто-то запустил скрипт, который почистил не ту директорию, или на сервере нашли посторонний процесс. Нужно понять: это была ошибка сотрудника, скомпрометированный ключ или злонамеренное действие изнутри. Логи говорят только «root выполнил команду в 03:14» — и точка.
- Спор о причине сбоя. Продакшен упал после изменения конфига nginx. В логах видно, что конфиг менялся, но не видно, кто именно его правил и когда именно — совпадает ли момент правки с моментом сбоя или это разные события.
- Увольнение сотрудника. Если весь доступ держится на одном общем пароле, при уходе человека нужно менять пароль для всей команды и заново раздавать — либо, что происходит на практике чаще, никто этого не делает вовремя. Похожая история разобрана в статье о том, как SSH-ключ уволенного сотрудника проработал восемь месяцев — доступ просто забыли отозвать, потому что не было чёткого списка «кому какой ключ принадлежит».
Во всех трёх случаях техническое решение — настроить персонализацию — стоит недорого. Дорого стоит его отсутствие в момент, когда оно нужно.
Именные учётные записи вместо общего root
Первый и самый важный шаг — у каждого члена команды своя системная учётная запись, а не общий root. Root при этом не выключается совсем (иногда он всё равно нужен), но перестаёт быть точкой входа для повседневной работы.
Создание пользователя с правом на sudo на Debian/Ubuntu:
adduser ivan
usermod -aG sudo ivan
На RHEL/AlmaLinux группа обычно называется wheel:
useradd -m maria
usermod -aG wheel maria
Дальше — самое главное для персонализации: вход только по именному SSH-ключу, не по паролю и не по общему ключу для всех. Каждому сотруднику генерируется его собственная пара ключей, публичный кладётся в ~/.ssh/authorized_keys его личной учётной записи:
mkdir -p /home/ivan/.ssh
echo "ssh-ed25519 AAAA... ivan@work-laptop" >> /home/ivan/.ssh/authorized_keys
chown -R ivan:ivan /home/ivan/.ssh
chmod 700 /home/ivan/.ssh
chmod 600 /home/ivan/.ssh/authorized_keys
В /etc/ssh/sshd_config стоит явно запретить прямой вход под root и парольную аутентификацию:
PermitRootLogin no
PasswordAuthentication no
После этого шага каждое подключение к серверу уже привязано к конкретному человеку на уровне sshd — это видно в journalctl -u ssh как Accepted publickey for ivan from ..., с указанием отпечатка именно его ключа.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверsudo с логированием: кто, что и когда
Именной вход решает половину задачи — видно, кто зашёл. Вторая половина — видно, что этот человек сделал после входа, если он получает root через sudo. По умолчанию каждый вызов sudo уже пишется в /var/log/auth.log (Debian/Ubuntu) или /var/log/secure (RHEL/AlmaLinux) с указанием пользователя и команды:
Aug 27 14:02:11 srv sudo: ivan : TTY=pts/0 ; PWD=/etc/nginx ; USER=root ; COMMAND=/usr/bin/vim nginx.conf
Это уже большой шаг вперёд по сравнению с общим root — видно имя пользователя и саму команду верхнего уровня. Но у голого лога sudo есть слепое пятно: если команда — это vim или bash, лог не покажет, что именно человек делал *внутри* редактора или интерактивной сессии. Здесь помогает встроенная в sudo запись ввода-вывода. В /etc/sudoers (через visudo, руками файл лучше не редактировать):
Defaults log_input, log_output
Defaults iolog_dir=/var/log/sudo-io/%{user}
После этого каждая sudo-сессия с интерактивной оболочкой записывается целиком — можно просмотреть её через sudo sudoreplay -l и воспроизвести конкретную сессию как терминальную запись:
sudoreplay -l
sudoreplay 00/00/01
Это буквально видеозапись того, что человек печатал и что видел на экране, с привязкой к его учётной записи и таймстампом. Для интернет-соединения с сервером log_output может немного повышать нагрузку на диск при активной работе — на практике для команды из нескольких человек это не заметно.
Отдельный нюанс: не превращайте sudo в бесправный root по одной опечатке в конфиге — известны реальные случаи, когда sudo без пароля и лишний символ в пути приводили к потере целого каталога. Логирование помогает разобраться постфактум, но не заменяет аккуратные права.
auditd: аудит на уровне ядра, а не только оболочки
sudo-логи и история команд видят то, что происходит через shell. Но если кто-то отредактировал файл напрямую через текстовый редактор, изменил права через chmod, или процесс что-то сделал в обход интерактивной сессии — это события ядра, и увидеть их может только auditd, подсистема аудита самого Linux-ядра.
Установка на Debian/Ubuntu:
apt install auditd audispd-plugins
systemctl enable --now auditd
На RHEL/AlmaLinux auditd обычно уже установлен по умолчанию, достаточно проверить, что служба запущена:
systemctl status auditd
Ключевые правила для задачи «кто что делал» — отслеживание изменений критичных файлов и выполнения команд с повышением привилегий. Добавляются в /etc/audit/rules.d/audit.rules:
# кто менял пользователей и права доступа
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k identity
-w /etc/sudoers.d/ -p wa -k identity
# кто менял SSH-конфигурацию
-w /etc/ssh/sshd_config -p wa -k sshd_config
# использование sudo и su
-w /usr/bin/sudo -p x -k privilege_escalation
-w /bin/su -p x -k privilege_escalation
После augenrules --load или перезапуска auditd правила активны, и любое совпадающее действие пишется в /var/log/audit/audit.log с указанием реального auid (audit user id) — идентификатора пользователя, который инициировал действие, даже если он потом переключился на root через sudo. Это ключевое отличие auditd от обычных логов: auid сохраняется через цепочку sudo/su и показывает исходного человека, а не текущий эффективный uid.
Искать по конкретному тегу удобно через ausearch:
ausearch -k identity --start today
ausearch -k privilege_escalation -ui 1001
Подробный разбор установки, отладки шумных правил и типичных ошибок — в отдельной статье про настройку auditd. Там же — как не утонуть в объёме событий, если правил слишком много.
Привязка истории команд к конкретному пользователю
Стандартный ~/.bash_history — слабое звено даже при именных учётках: он пишется на диск только при выходе из сессии, легко редактируется или чистится самим пользователем (history -c), и не хранит, под каким именно логином команда была выполнена, если несколько человек по очереди переключаются в root через sudo su -.
Первое улучшение — писать историю сразу, с таймстампом, и запретить её тривиальное стирание. В /etc/profile.d/history.sh:
export HISTTIMEFORMAT="%F %T "
export PROMPT_COMMAND="history -a; ${PROMPT_COMMAND}"
export HISTSIZE=10000
export HISTFILESIZE=10000
readonly HISTFILE
history -a дописывает каждую команду в файл истории сразу после выполнения, а не только при закрытии сессии — так history -c внутри активной сессии не спасает от уже записанных строк. Но это по-прежнему история конкретного пользователя на конкретной машине — если систему после инцидента переустановят или диск подменят, история потеряется вместе с ней.
Второе, более надёжное решение — отправлять каждую выполненную команду сразу во внешний syslog через PROMPT_COMMAND, с указанием пользователя, из-под которого команда реально была запущена:
export PROMPT_COMMAND='RETRN_VAL=$?; logger -p local6.info "USER=$(whoami) LOGNAME=$LOGNAME PWD=$PWD CMD=$(history 1 | sed "s/^ *[0-9]* *//")"; ${PROMPT_COMMAND}'
Это простое решение, но оно перекрывает ключевую дыру: даже если человек внутри root-сессии, LOGNAME продолжает указывать на исходного пользователя, который запустил sudo su -, и запись уходит в системный лог сразу, а не только в локальный файл истории, который можно почистить. Для более строгого аудита интерактивных сессий вместо самописного логирования лучше полагаться на sudo log_input/log_output, описанный выше, — он не полагается на то, что пользователь не отредактирует свой .bashrc.
Централизованный сбор логов: чтобы их нельзя было стереть локально
Все описанные механизмы решают половину задачи — кто и что сделал. Вторая половина — чтобы эти записи нельзя было уничтожить с того же сервера, где они создавались. Если единственная копия audit.log и auth.log лежит на скомпрометированной машине, у злоумышленника с root-доступом есть все возможности их подчистить.
Практическое решение — отправлять логи на отдельный сервер сразу, по мере появления, через rsyslog. На стороне источника, в /etc/rsyslog.d/50-remote.conf:
*.* @@log-collector.internal:514
Двойной @@ означает TCP вместо UDP — так строки не теряются при обрыве сети. Для auditd отдельно настраивается плагин audisp-remote, который может пересылать события аудита на удалённый сервер аудита независимо от общего syslog-потока — это отдельная, более специализированная цепочка именно для событий безопасности.
На принимающей стороне логи стоит хранить в режиме, близком к append-only — как минимум с отдельными правами доступа, куда обычные администраторы серверов-источников не имеют записи. Общий подход к сбору логов с нескольких машин в одно место, включая варианты через Graylog или Grafana Loki, — тема отдельного разбора; здесь важно только то, что коллектор должен быть физически другим сервером с другим кругом администраторов.
Минимальный практический ориентир: если у вас три-пять серверов и команда до десяти человек, отдельная VPS под коллектор логов с 2 vCPU и умеренным диском под ротацию — вполне рабочая отправная точка; конкретные цифры нагрузки зависят от объёма команд и правил auditd, поэтому стоит смотреть на реальный поток, а не ориентироваться на чужие бенчмарки.
Почему это работает только на счёт «до», а не «после»
Здесь стоит сказать прямо: ничего из описанного выше нельзя настроить постфактум и получить те же данные. Если инцидент уже случился, а общий root и общая история команд стояли всё это время — расследовать нечего, потому что нечего смотреть. auditd, включённый после инцидента, не покажет, что происходило до его включения. Именные учётки, заведённые после ухода сотрудника, не скажут, что он делал, пока имел общий доступ.
Это разница между инцидентом, который можно разобрать за час по логам, и инцидентом, который разбирают неделями по косвенным признакам — или не разбирают вовсе, потому что улик просто нет. Формальный процесс разбора инцидента возможен только если инфраструктура заранее умела отвечать на вопрос «кто и когда».
Практический вывод простой: настройка персонального доступа и аудита — это не разовая задача уровня «сделаем как-нибудь», а базовая гигиена, которая встаёт в строй одновременно с самим сервером. Если в команде уже сложилась практика общего root, лучше не ждать удобного момента, а выделить пару часов и провести миграцию на именные учётки сейчас, пока это плановая задача, а не пожарная. Общая структура организации доступа для команды — то есть кто получает какие права, как это фиксируется и пересматривается — разобрана в статье как оформить доступ сотрудников к продакшену, а конкретно про переход с общего root на раздачу персональных прав — в материале как раздать доступ команде без выдачи root.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Обязательно ли полностью убирать root-доступ, если у меня всего один-два администратора?
Не обязательно исключать root совсем, но даже при двух администраторах именные учётки и sudo-логи стоят внедрения: как минимум они снимают вопрос «кто из нас двоих» при любом споре, а стоит это буквально пара команд на настройку.
Не проще ли просто вести журнал вручную — кто что делал и когда?
Ручной журнал работает, пока все добросовестно его заполняют и не забывают в спешке. Технический аудит не полагается на дисциплину — он пишет события автоматически и не зависит от того, вспомнил ли человек сделать запись.
auditd сильно нагружает сервер?
При разумном наборе правил (несколько -w на конкретные файлы и бинарники, а не аудит всех системных вызовов подряд) нагрузка на CPU и диск обычно малозаметна на фоне остальной работы сервера. Если правил становится много и добавляется отслеживание широких категорий вызовов, стоит проверить реальную нагрузку на своей машине — общих цифр, которые подойдут всем, здесь нет.
Что делать с уже существующими общими ключами, если менять всё сразу страшно?
Мигрировать постепенно: сначала завести именные учётки и ключи для всех, оставить общий доступ как резервный на переходный период, затем отключить его явно — PermitRootLogin no и отзыв общего ключа — и зафиксировать дату отключения, чтобы было видно, с какого момента данные аудита полны.
Нужно ли это, если сервер один и команда маленькая?
Да, если сервером пользуется больше одного человека. Разница между «полезно» и «критично» — не в размере команды, а в том, будет ли когда-нибудь вопрос «кто это сделал» — а он рано или поздно возникает почти всегда.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →