Como posso manter um registro local com carimbo de data e hora de todos os comandos remotos que uso ssh
(cliente openssh da linha de comando iniciado por meio de bash
)?
Requisitos:
Essencial:
- 100% do lado do cliente sem depender do log do servidor
- Configurado ou instalado por usuário com logs armazenados no diretório inicial do usuário.
- Suporte para distinguir entre múltiplas sessões simultâneas com vários usuários e hosts.
- Não intrusivo (não há necessidade de ativá-lo todas as vezes e não interfere significativamente no uso do ssh)
Prioridade máxima:
- A saída não é registrada ou filtrada tanto quanto possível
- As entradas de senha não são registradas ou o arquivo está criptografado
- Indica os comandos reais usados (após a conclusão da tabulação/histórico, backspaces, CTRL+ C, etc... terem sido processados)
Bom ter:
- Também registra comandos em sessões encadeadas (comandos inseridos durante sessões remotas
ssh
ou remotassu <user>
) - O início e o fim da sessão devem ser registrados
- Uma
bash
solução simples e não raiz seria melhor (talvez umalias
scriptbash
wrapper para ossh
comando?)
- Também registra comandos em sessões encadeadas (comandos inseridos durante sessões remotas
Meu nível de habilidade:
- Não sou novo em programação, mas ainda estou aprendendo
bash
o "jeito Linux", então exemplos de código com breves explicações seriam muito apreciados.
Estratégias possíveis
- registrador de teclas-- Problema: registra senhas, não registra conclusão de guia/histórico (vejaresposta de Glenn)
screen
com scrollback despejando uma vez por segundo ediff
entre eles para encontrar novas linhas de scrollback-- Problema: como isso pode ser implementado de uma forma automatizada e útil?ssh "$@" | tee >(some_cleaner_function >> $logfile)
- Problema: não é possível lidar com comandos multilinha ou histórico em sessões encadeadas, é necessária uma limpeza cuidadosa (veja minha resposta)- Uma combinação de alguns dos itens acima
Um exemplo
A seguinte sessão 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
Pode resultar em um log ~/logs/ssh.log
como:
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]
Ou talvez um log separado seja criado para cada sessão com a linha de comando usada para iniciar a sessão na parte superior do arquivo.
Responder1
Fiquei intrigado com sua pergunta. Originalmente, eu não ia dar uma resposta, mas fiquei viciado.
Isso usa expect
e é realmente um keylogger.
#!/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}"
Notas:
- ele é anexado a um arquivo com o destino ssh no nome
- é um key logger: se você não configurou chaves ssh, você obtém a senha do usuário no arquivo de log
- é frustrado pela conclusão da tabulação: se o usuário digitar uptTab(para o
uptime
comando), você obterá "upt\t" no arquivo de log, não "uptime" - ele captura caracteres no modo "bruto": se o usuário for um digitador ruim, você obterá muitos
^?
(caracteres de retrocesso) no arquivo de log.
Responder2
Atualmente estou usando o script bash abaixo. Ele tem muitos problemas, mas é a única solução que encontrei que atende a todos os requisitos, prioridades e "bom para quem tem" (pelo menos na maioria das vezes).
Esta respostadiscute por que registrar sessões ssh localmente é tão difícil.
Problemas com o script que encontrei até agora:
Comandos multilinha causam problemas:
- Se você percorrer um item de múltiplas linhas no histórico remoto (com as teclas para cima/para baixo), ele registrará um item do histórico em vez do comando mais recente. Você pode evitar issoexcluindo do histórico do bashquaisquer comandos multilinha imediatamente após serem usados.
- Somente a primeira linha dos comandos multilinhas é registrada.
Sessões encadeadas (usando comandos
ssh
ousu
na extremidade remota) fazem com que a rolagem do histórico registre comandos passados pela rolagem em vez dos comandos reais usadosAs expressões regulares podem ser melhoradas e talvez precisem ser modificadas para determinados ambientes:
- Eu trapaceio convertendo caracteres não imprimíveis
cat -v
antes de limpar. Como resultado, o conteúdo válido pode ser removido se você usar strings como^[[
em seus comandos. - Às vezes, você obtém entradas extras registradas antes do comando, como se você folheasse o histórico muito rapidamente. Geralmente é seguido por um "^M" antes do comando real e, portanto, pode ser removido, se desejado.
- Às vezes ocorrem outros caracteres de controle. Estou deixando todos eles por enquanto até saber quais são seguros para remover. ^M, como acabei de mencionar, é útil para detectar entradas registradas inválidas e ^C informaria se o comando foi abortado.
- O prompt regex pode precisar ser modificado para prompts específicos, e posso imaginar que diferentes ambientes remotos podem ter diferentes padrões de caracteres de controle.
- Eu trapaceio convertendo caracteres não imprimíveis
Nenhuma conclusão do comando ssh, como para o nome do host.Você pode obter a conclusão do bash se você usar o alias deste scriptssh
comalias ssh="sshlog"
Fonte e instalação do script:
Para instalar, cole o seguinte em ~/bin/sshlog e torne-o executável. Ligue com sshlog <ssh command options>
. Opcionalmente, alias para 'ssh' no arquivo .bashrc do usuário.
#!/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}
Exemplo de conteúdo de registro:
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]
Responder3
Que tal strace -o /tmp/ssh_log -ff -s8192 -T -ttt -fp $(pidof sshd)
? Isso registra todas as sessões ssh. Você pode precisar de uma ferramenta para analisar o log posteriormente ou apenas usar grep
etc.awk
-f
: rastrear crianças bifurcadas-ff
: registre cada filho separadamente emssh_log.PID
-s8192
: aumente o limite de registro de strings (se necessário)-T -ttt
: carimbo de microssegundos em segundos desde a Época-p N
: anexar ao pidN
Responder4
Tenho uma resposta menos complicada e certamente não é um keylogger. Não entendi o que você quer dizer sobre ser independente do log do servidor (isso significa que todas as ações precisam ser executadas no servidor e todos os logs são logs do lado do servidor) e, portanto, pensei que uma boa ideia seria passar para todo o sistema bashrc um comando prompt como:
PROMPT_COMMAND='history -a >(tee -a ~/.bash_history | logger -t "$USER[$$] $SSH_CONNECTION")'
No debian você deve editar o arquivo: /etc/bash.bashrc e no centos o arquivo: /etc/bashrc
Se quiser iniciar o registro da sessão em que você está, você deve obter o arquivo que editou, por exemplo, executar:
source /etc/bash.bashrc
em um sistema debian ou
source /etc/bashrc
em um sistema centos.
De agora em diante, todos os comandos de todas as sessões ssh serão registrados em/var/log/syslogem um sistema debian, e em/var/log/mensagensem um sistema centos.
Caso você queira registrá-los em um arquivo separado e não bagunçar outros arquivos de log, você pode usar:
PROMPT_COMMAND='history -a >(tee -a ~/.bash_history | logger -p local6.info -t "$USER[$$] $SSH_CONNECTION")'
em vez do exemplo PROMPT_COMMAND anterior e configure o rsyslogd conforme necessário.
Por exemplo, em um sistema Debian, edite o/etc/rsyslog.confarquivo: altere a linha:
.;auth,authpriv.none -/var/log/syslog
para
.;auth,authpriv.none,local6 -/var/log/syslog
e adicione a seguinte linha ao final do arquivo:
local6.info /var/log/history.log
então execute:
touch /var/log/history.log && /etc/init.d/rsyslog restart