所有 ssh 命令的本地时间戳记录?

所有 ssh 命令的本地时间戳记录?

如何保留我在ssh(命令行 openssh 客户端通过 启动bash)中使用的所有远程命令的本地带时间戳记录?

要求:

  • 基本的:

    • 100% 客户端,不依赖服务器日志记录
    • 按用户配置或安装,日志存储在用户的主目录中。
    • 支持区分与不同用户和主机的多个同时会话。
    • 非侵入式(无需每次激活,不会显着干扰 ssh 的使用)
  • 高优先级:

    • 要么不记录输出,要么尽可能过滤掉输出
    • 未记录密码条目或文件已加密
    • 指示实际使用的命令(制表符/历史记录完成后、退格键、CTRL+C等...已被处理)
  • 很高兴有:

    • 还记录链接会话中的命令(在远程sshsu <user>会话期间输入的命令)
    • 应记录会话开始和结束
    • 一个简单的bash基于非 root 的解决方案将是最好的(也许是命令的aliasbash包装脚本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 会话如此困难。

到目前为止我发现的脚本问题:

  1. 多行命令会导致问题:

    • 如果您翻阅远程历史记录中的多行项目(使用向上/向下键),它将记录历史记录项目而不是最新命令。您可以通过以下方式避免这种情况从 bash 历史记录中删除任何多行命令在使用后立即执行。
    • 仅记录多行命令的第一行。
  2. 链接会话(在远程端使用sshsu命令)会导致历史记录滚动以记录滚动传递的命令,而不是实际使用的命令

  3. 正则表达式可以改进,并且可能需要针对某些环境进行修改:

    • 我通过在cat -v清洁之前转换非打印字符来作弊。因此,如果您曾经^[[在命令中使用过字符串,则可能会删除有效内容。
    • 有时,您会在命令之前获得额外的记录输入,例如如果您非常快速地翻阅历史记录。这通常在实际命令之前跟随一个“^M”,因此如果需要的话可以将其删除。
    • 有时会出现其他控制字符。我暂时将它们全部留在里面,直到我知道哪些可以安全移除。我刚才提到的 ^M 对于检测无效的记录输入很有用,并且 ^C 会告诉您命令是否被中止。
    • 提示正则表达式可能需要针对特定​​提示进行修改,并且我可以想象不同的远程环境可能具有不同的控制字符模式。
  4. 没有 ssh 命令 bash 补全,例如主机名。 ssh如果将此脚本别名为with,则可以获得 bash 补全alias ssh="sshlog"

脚本来源及安装:

要安装,请将以下内容粘贴到 ~/bin/sshlog 并使其可执行。致电sshlog <ssh command options>.可以选择在用户的 .bashrc 文件中将其别名为“ssh”。

#!/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 会话。您可能需要一个工具来解析日志,或者只是使用等等grepawk

  • -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/系统日志在 Debian 系统上,并且在/var/日志/消息在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

相关内容