所有 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

相關內容