如何保留我在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 會告訴您命令是否被中止。
- 提示正規表示式可能需要針對特定提示進行修改,並且我可以想像不同的遠端環境可能具有不同的控製字元模式。
- 我透過在
沒有 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 會話。您可能需要一個工具來解析日誌,或者只是使用等等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/系統日誌在 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