Всем известно о существовании .bash_history файла (или .zsh_history, например). Туда сохраняются команды, введённые залогинеными пользователями. По истории можно искать, запуская команды повторно как есть или исправив их, при необходимости. Всем, кроме того, известно, что, при входе на сервер с помощью SSH, данные о логине, да и вообще все попытки логина, сохраняются в лог, в Ubuntu это /var/log/auth.log.

В какой-то момент потребовалось сделать нечто среднее между первым и вторым, объединив в единый лог данные о пользователях, которые входили на сервер, с сохранением данных о том, под каким логином был вход, с данными о том, какие команды были ими запущены после входа. Кроме того, есть пользователь на сервере, под которым могут заходить другие пользователи со своими SSH ключами. В этом случае, хотелось логировать еще и данными о том, что за пользователь заходил. То есть, условно говоря, нужны были следующие данные: локальный (для сервера) пользователь, под которым зашли, удаленный пользователь, команды, им запускаемые.

Что можно использовать для сбора таких данных?

Первый на ум приходит audit. Пакет есть в каждом дистрибутиве Linux, проблем с установкой и настройкой нет (кроме огромного количества настроек), благо различных how-to, статей и т.д. написано неимоверное количество. Одна проблема: очень-очень подробная информация о происходящем в системе. Хотелось чего-то более просто и наглядного, с возможностью настраивать то, что должно быть записано в лог. Может быть я недостаточно хорошо читал документацию, но я так и не понял, как сделать так, чтобы в лог попадало только то, что мне необходимо. Кстати, пока писал, GitHub напомнил мне, что есть альтернатива, написанная на Golang - go-audit, но её пока не проверял.

Вторая утилита - acct. Но, в данном случае, собирается, скорее, очень общая информация. Если интересует более подробная информация, можно посмотреть здесь.

Итак, что можно сделать для получения нужного результата? Для начала попробуем как-то указать и сохранить для дальнейшего использования какой-то “идентификатор” пользователя, который хочет зайти на сервер. Сделать это можно, немного изменив данные, которые хранятся в ~/.ssh/authorized_keys. Пример записи в authorized_keys:

ssh-rsa <public-key> <username>

Но если изменить это следующим образом:

environment="LC_SSH_USER=user" ssh-rsa <public-key> <username>

то после входа пользователя на сервер, в переменных окружения будет:

user@server:~> echo $LC_SSH_USER
user

Чтобы это сработало, потребуется также убедиться, что в /etc/ssh/sshd_config есть строка PermitUserEnvironment yes. Это не совсем безопасная настройка, поэтому проверьте, что в файле есть так же ограничение на передачу переменных от клиента: AcceptEnv LANG LC_*.

Файл history - важная часть. Именно оттуда будем брать сведения о запущенных командах.

Кстати, здесь сделаю небольшое отступление. При желании можно сделать отдельный файл history для каждого пользователя, который заходит на сервер. Само собой, это имеет смысл только в том случае, если локальный пользователь на сервере только один и именно он используется для входа. Это не очень правильно, но я знаю, что в некоторых местах используется именно этот вариант - один “технический” пользователь, под которым все заходят, если необходимо совершить какие-то операции на сервере. Сделать это можно, указав HISTFILE переменную, например, в ~/.bashrc:

HISTFILE=~/.bash_history."$LC_SSH_USER"

Итак, вернемся к изначально задуманному решению. Я написал скрипт, составив его из разных вариантов, которые нашёл на просторах Интернета. Скрипт следующий:

HISTTIMEFORMAT="%d/%m/%y %T: "
PROMPT_COMMAND="history -a;$PROMPT_COMMAND"

if [ "$LC_SSH_USER" != "" ]; then
  logger -ip local7.info -t auth-audit "Auth request: $LC_SSH_USER (remote) -> $USER (local) "
fi

OLD_CMD=""
function log2syslog
{
    declare CMD
    declare P_DIR
    CMD=$(history 1 | { read a b c d; echo $d; })
    P_DIR=$(pwd)
    if [[ $CMD != "" && $CMD != $OLD_CMD && $OLD_CMD != "" ]]; then
       logger -p local7.info -t cmd-audit -i -- "USER=${USER}(${LC_SSH_USER}) PWD=${P_DIR} CMD='${CMD}'"
    fi
    OLD_CMD=$CMD
}
trap log2syslog DEBUG || EXIT

Коротко пройдусь по тому, что содержится в скрипте. HISTTIMEFORMAT добавляет таймстамп в history файл. Проверяется, установлена ли переменная окружения LC_SSH_USER и если да, то отправляем в лог данные о входе:

Mar 12 07:23:26 server auth-audit[23617]: Auth request: alex (remote) -> user (local)

Далее читается последняя запущенная команда и также отправляется в лог:

Mar 12 07:23:32 server cmd-audit[23786]: USER=user(alex) PWD=/home/user CMD='df -h'

Для пользователя root это тоже работает:

Mar 12 07:23:26 server auth-audit[23617]: Auth request: alex (remote) -> user (local)
Mar 12 07:23:30 server auth-audit[23704]: Auth request: alex (remote) -> root (local)
Mar 12 07:23:32 server cmd-audit[23786]: USER=root(alex) PWD=/root CMD='df -h'

Скрипт нужно поместить в /etc/profile.d/, например, как /etc/profile.d/audit.sh.

Дополнительно, потребуется конфиг для rsyslog:

# Send logs to history.log
local7.*        /var/log/history.log

# This setting prevents sending logs in syslog
&stop

Его отправить в /etc/rsyslog.d и перезапустить rsyslog сервис. Кроме того, стоит настроить logrotate, чтобы контролировать место, занимаемое файлом с логами.

Всё описанное не может и не должно рассматриваться как вариант обеспечения какой-либо безопасности или серьёзного аудита пользовательской активности. Пользователи, при желании, могут вмешаться в процесс работы скрипта или изменить файл history, либо отключить полностью или частично запись в history`. Способов для этого много:

set +o history

либо

unset HISTFILE

либо

HISTFILE=/dev/null

плюс еще некоторые варианты.

В нашем случае было необходимо просто записывать команды, запущенные пользователями, на случай, если кто-то внёс какие-либо изменения не уведомив остальную команду, или были сделаны какие-то тестовые изменения, про которые потом все забыли :) И тут history.log сможет помочь и вспомнить, что делалось.