Как мне сохранить локальную запись с временными метками всех удаленных команд, которые я использую ssh
(клиент командной строки openssh запущен через bash
)?
Требования:
Существенный:
- 100% клиентская часть без необходимости ведения журналов на сервере
- Настраивается или устанавливается для каждого пользователя, журналы хранятся в домашнем каталоге пользователя.
- Поддержка различения нескольких одновременных сеансов с разными пользователями и хостами.
- Ненавязчивый (нет необходимости активировать его каждый раз и не оказывает существенного влияния на использование ssh)
Высокий приоритет:
- Либо вывод не регистрируется, либо фильтруется по возможности.
- Либо ввод пароля не регистрируется, либо файл зашифрован.
- Указывает фактически использованные команды (после завершения табуляции/истории, возврата на одну позицию, CTRL+ Cи т. д. были обработаны)
Приятно иметь:
- Также регистрирует команды в связанных сеансах (команды, введенные во время удаленных
ssh
сеансовsu <user>
) - Начало и конец сеанса должны регистрироваться.
bash
Лучшим решением будет простое решение без полномочий root (возможно,alias
скриптbash
-оболочка дляssh
команды?)
- Также регистрирует команды в связанных сеансах (команды, введенные во время удаленных
Мой уровень мастерства:
- Я не новичок в программировании, но все еще учусь
bash
«пути Linux», поэтому примеры кода с краткими пояснениями были бы весьма признательны.
Возможные стратегии
- кейлоггер-- Проблема: регистрирует пароли, не регистрирует заполнение вкладок/истории (см.ответ гленна)
screen
с прокруткой дампа один раз в секунду иdiff
между ними для поиска новых строк прокрутки-- Проблема: как это можно реализовать в автоматизированном режиме?ssh "$@" | tee >(some_cleaner_function >> $logfile)
-- Проблема: невозможно обработать многострочные команды или историю в связанных сеансах, необходима тщательная очистка (см. мой ответ)- Сочетание некоторых из вышеперечисленных вариантов
Пример
Следующий сеанс SSH:
user@local:~$ ssh user@remote
Last login: Tue Jun 17 16:34:23 2014 from local
user@remote:~$ cd test
user@remote:~/test$ ls
a b
user@remote:~/test$ exit
Может привести к появлению журнала, ~/logs/ssh.log
например:
2014-06-17 16:34:50 [user@remote - start]
2014-06-17 16:34:51 [user@remote] cd test
2014-06-17 16:34:52 [user@remote] ls
2014-06-17 16:34:53 [user@remote] exit
2014-06-17 16:34:53 [user@remote - end]
Или, возможно, для каждого сеанса будет создан отдельный журнал с командной строкой, используемой для запуска сеанса, в верхней части файла.
решение1
Меня заинтриговал ваш вопрос. Изначально я не собирался отвечать, но меня зацепило.
Это приложение использует expect
и на самом деле является кейлоггером.
#!/usr/bin/expect -f
proc log {msg} {
puts $::fh "[timestamp -format {%Y-%m-%d %H:%M:%S}]: $msg"
}
set ssh_host [lindex $argv 0]
set ::fh [open "sshlog.$ssh_host" a]
log "{session starts}"
spawn ssh $ssh_host
interact {
-re "(.)" {
set char $interact_out(1,string)
if {$char eq "\r"} {
log $keystrokes
set keystrokes ""
} else {
append keystrokes $char
}
send -- $char
}
eof
}
log "{session ends}"
Примечания:
- он добавляется к файлу с указанием назначения ssh в имени
- это кейлоггер: если вы не настроили ключи ssh, вы получите пароль пользователя в файле журнала
- это блокируется автодополнением: если пользователь вводит uptTab(для
uptime
команды), в файле журнала вы получите "upt\t", а не "uptime" ^?
он захватывает символы в «сыром» режиме: если пользователь плохо печатает, в файле журнала будет много (символов возврата на одну позицию).
решение2
В настоящее время я использую скрипт bash ниже. Он имеет много проблем, но это единственное решение, которое я нашел, которое отвечает всем требованиям, приоритетам и "приятно иметь" (по крайней мере, большую часть времени).
Этот ответобсуждает, почему так сложно вести локальный журнал сеансов SSH.
Проблемы со скриптом, которые я обнаружил на данный момент:
Многострочные команды вызывают проблемы:
- Если вы пролистаете многострочный элемент в удаленной истории (с помощью клавиш вверх/вниз), он запишет элемент истории вместо последней команды. Вы можете избежать этого,удаление из истории bashлюбые многострочные команды сразу после их использования.
- Регистрируется только первая строка многострочных команд.
Связанные сеансы (использующие команды
ssh
илиsu
на удаленном конце) приводят к тому, что при прокрутке истории записываются прокрученные переданные команды вместо фактически использованных команд.Регулярные выражения можно улучшить и, возможно, потребуется изменить для определенных сред:
- Я обманываю, преобразуя непечатаемые символы с помощью
cat -v
перед очисткой. В результате допустимый контент может быть удален, если вы когда-либо используете строки, подобные^[[
вашим командам. - Иногда вы получаете дополнительный ввод перед командой, например, если вы очень быстро просматриваете историю. Обычно за этим следует "^M" перед фактической командой, и поэтому его можно убрать, если это необходимо.
- Иногда встречаются и другие управляющие символы. Я оставляю их все, пока не узнаю, какие из них можно безопасно удалить. ^M, как я уже упоминал, полезен для обнаружения недопустимого записанного ввода, а ^C сообщит вам, была ли команда прервана.
- Регулярное выражение подсказки может потребоваться изменить для конкретных подсказок, и я могу предположить, что в разных удаленных средах могут быть разные шаблоны управляющих символов.
- Я обманываю, преобразуя непечатаемые символы с помощью
Нет автодополнения bash-команды ssh, например, для имени хоста.Вы можете получить автодополнение bash, если присвоите этому скриптуssh
псевдонимalias ssh="sshlog"
Исходный код скрипта и установка:
Для установки вставьте следующее в ~/bin/sshlog и сделайте исполняемым. Вызовите с помощью sshlog <ssh command options>
. При желании укажите псевдоним 'ssh' в файле .bashrc пользователя.
#!/bin/bash
# A wrapper for the ssh command that produces a timestamped log of all ssh commands
declare -r logfile=~/logs/ssh.log
declare -r description="sshlog-${$} ${@}"
declare -r TAB=$'\t'
logdir=`dirname ${logfile}`
[ -d ${logdir} ] || mkdir "${logdir}";
clean_control_chars() {
while IFS= read -r line; do
# remove KNOWN control characters. Leave the rest for now.
# line=$(echo "${line}" | sed 's/\^\[\[K//g') # unkown control character: ^[[K
# line=$(echo "${line}" | sed 's/\^\[\[[0-9]\+[P]//g') # these are generated by up/down completion - e.g. ^[[2P
line=$(echo "${line}" | sed 's/\^\[\[[0-9]*[A-Z]//g') # all other ^[[..
# replay character deletions (backspaces)
while [[ $(echo "${line}" | grep -E --color=never '.\^H') != "" ]]; do
line=$(echo "${line}" | sed 's/.\^H//')
done
# remove common control characters
line=$(echo "${line}" | sed 's/\^M$//') # remove end of line marker from end
line=$(echo "${line}" | sed 's/^\^G//g') # remove start marker from start
# remove ^G from other locations - possibly a good idea
# line=$(echo "${line}" | sed 's/\^G//g')
# remove all other control characters - not recommended (many like ^C and ^M indicate which section was processed/ ignored)
# line=$(echo "${line}" | sed 's/\^[A-Z]//g')
echo ${line};
done
}
filter_output() {
while IFS= read -r line; do
# convert nonprinting characters and filter out non-prompt (in Ubuntu 14.04 tests, ^G indicates prompt start)
line=$(echo "${line}" | cat -v | grep -Eo '[\^][G].*[\$#].*')
[[ ${line} != "" ]] && echo "${line}"
done
}
format_line() {
while IFS= read -r line; do
raw=${line};
line=$(echo "${line}" | clean_control_chars);
prompt=$(echo "${line}" | grep -Po '^.*?(\$|#)[\s]*')
command=${line:${#prompt}}
timestamp=`date +"%Y-%m-%d %H:%M:%S %z"`
echo -e "${timestamp}${TAB}${description}${TAB}${prompt}${TAB}${command}"
done
}
echo "Logging ssh session: ${description}"
echo "[START]" | format_line >> ${logfile}
/usr/bin/ssh "$@" | tee >(filter_output | format_line >> ${logfile})
echo "[END]" | format_line >> ${logfile}
Пример содержания журнала:
2014-06-29 23:04:06 -0700 sshlog-24176 remote [START]
2014-06-29 23:04:12 -0700 sshlog-24176 remote oleg@remote:~$ cd test
2014-06-29 23:04:13 -0700 sshlog-24176 remote oleg@remote:~/test$ ls
2014-06-29 23:04:14 -0700 sshlog-24176 remote oleg@remote:~/test$ exit
2014-06-29 23:04:14 -0700 sshlog-24176 remote [END]
решение3
Как насчет strace -o /tmp/ssh_log -ff -s8192 -T -ttt -fp $(pidof sshd)
? Это регистрирует все сеансы ssh. Вам может понадобиться инструмент для последующего анализа журнала или просто используйте grep
, awk
и т. д.
-f
: трассировка разветвленных потомков-ff
: регистрируйте каждого ребенка отдельноssh_log.PID
-s8192
: увеличить лимит записи в журнал строк (при необходимости)-T -ttt
: микросекундная маркировка в секундах с начала эпохи-p N
: прикрепить к pidN
решение4
У меня есть менее сложный ответ, и уж точно не кейлоггер. Я не понимаю, что вы имеете в виду независимость от серверного журнала (это означает, что все действия должны выполняться на сервере, и все журналы являются журналами на стороне сервера), и поэтому я подумал, что хорошей идеей будет передать в системный bashrc команду приглашения типа:
PROMPT_COMMAND='history -a >(tee -a ~/.bash_history | logger -t "$USER[$$] $SSH_CONNECTION")'
В Debian вам следует отредактировать файл: /etc/bash.bashrc, а в CentOS файл: /etc/bashrc
Если вы хотите начать ведение журнала текущего сеанса, вам необходимо указать файл, который вы отредактировали, например, выполнить:
source /etc/bash.bashrc
в системе Debian или
source /etc/bashrc
в системе CentOS.
С этого момента каждая команда каждого сеанса SSH будет регистрироваться в/var/log/syslogв системе Debian и в/var/log/сообщенияв системе Centos.
Если вы хотите записать их в отдельный файл и не мешать другим файлам журналов, вы можете использовать:
PROMPT_COMMAND='history -a >(tee -a ~/.bash_history | logger -p local6.info -t "$USER[$$] $SSH_CONNECTION")'
вместо предыдущего примера PROMPT_COMMAND, а затем настройте rsyslogd по мере необходимости.
Например, в системе Debian отредактируйте/etc/rsyslog.confфайл: измените строку:
.;auth,authpriv.none -/var/log/syslog
к
.;auth,authpriv.none,local6 -/var/log/syslog
и добавьте следующую строку в конец файла:
local6.info /var/log/history.log
затем выполните:
touch /var/log/history.log && /etc/init.d/rsyslog restart