我们日常在运维linux主机时,有时候由于某些突发问题,想排查这台主机的操作记录,我们可以通过history命令去查看。
但原生的history命令,默认显示的内容太少,只显示操作的命令行指令,无法看到其他更详细的信息,比如操作的时间、操作的用户、登录的源IP等。
本文就详细介绍一种方法,通过在/etc/profile或者/etc/bashrc文件中,定义环境变量,可以将linux主机的详细操作记录保存在日志文件中。下文分享我在项目中从最原始的版本逐渐优化到最终完善版本,共经历了4个版本。
用到的核心环境变量:HISTTIMEFORMAT 和 PROMPT_COMMAND
本文演示系统环境为:centos,本文介绍方法适合绝大部分linux系统,个别linux系统部分变量可能获取有问题。
01 初始版本
在/etc/bashrc(也可以在/etc/profile)文件末尾,添加以下内容:
### written by Jepson ###mkdir -m 777 -p /.cmdlog 2>&-export CMDLOG_FILE="/.cmdlog/cmdlog.$(date +%F)"[ -f $CMDLOG_FILE -a -s $CMDLOG_FILE ] || install -m 777 /dev/null $CMDLOG_FILE 2>&-LOGIN_TIMESTAMP=`date +%s`export PROMPT_COMMAND='{ date "+%F %T ### $(whoami)@${SSH_TTY} ### $(echo ${SSH_CONNECTION}) ### ${PWD} ### $LOGIN_TIMESTAMP ### $(history 1|awk "{\$1=\"\";print}") "; } 2>&- >> $CMDLOG_FILE'### end ###
保存后,执行 source /etc/bashrc 使其生效。
实现效果:在/.cmdlog目录下,创建一个名为 “cmdlog.日期”的日志文件。
记录的日志内容字段主要包括:操作时间、登录用户、ssh链接信息、当前操作目录、会话时间戳、操作指令
日志记录如下样例:
注意:这种方法能解决大部分需求,日志记录也包含了重要的信息(谁、什么时间、从哪里来的、连的哪台主机、在什么目录下、执行了什么命令)。
但后来在项目实践中发现linux 部分系统中,通过su切换用户后,SSH_CONNECTION 变量将无法获取到,因此将影响对于连接服务器的源IP和目的IP的获取,改造的采集方法如下:
编辑 /etc/bashrc文件,最后写入以下内容:### written by Jepson ###mkdir -m 777 -p /.cmdlog 2>&-declare -r HISTTIMEFORMAT='%F %T ### 'declare -r SSHTTY=$(who am i |awk '{print $2}')if [ "$SSH_CONNECTION" ];then declare -r SSH_CLIENTIP=$(echo $SSH_CONNECTION |awk '{ print $1}') declare -r SSH_HOSTIP=$(echo $SSH_CONNECTION |awk '{ print $3}') else declare -r SSH_CLIENTIP=$(who am i|awk '{print $5}' |sed 's/[()]//g') declare -r SSH_HOSTIP=$(ip addr | grep inet| grep -v 127.0.0.1 | grep -v inet6 |grep -v virbr| head -n 1 | awk -F/ '{print $1}' | awk '{print $2}')fideclare -r CMDLOG_FILE="/.cmdlog/cmdlog.$(date +%F)"[ -f $CMDLOG_FILE -a -s $CMDLOG_FILE ] || install -m 777 /dev/null $CMDLOG_FILE 2>&-declare -r LOGIN_TIMESTAMP=`date +%s`declare -r PROMPT_COMMAND='{ date "+%F %T ### ${HOSTNAME} ### ${USER} ### ${SSHTTY} ### ${SSH_CLIENTIP} ### ${SSH_HOSTIP} ### ${SSH_CONNECTION} ### ${PWD} ### $LOGIN_TIMESTAMP ###$(history 1|awk "{\$1=\"\";print}") "; } 2>&- >> $CMDLOG_FILE'### end ###
本次优化,解决因su切换用户后,获取不到ssh_connection信息。增加了通过其他方式获取源IP和目的IP,同时保留了ssh_connection信息,日志样例如下:上述版本运行一段时间后,又发现了新的问题:在命令行中执行回车键后,上一步操作记录会被重复记录一次,导致日志中存在大量的重复记录。### written by Jepson ###mkdir -m 777 -p /.cmdlog 2>&-declare -r HISTTIMEFORMAT='%F %T ### 'declare -r HISTCONTROL=''RSSHTTY=$(who am i |awk '{print $2}')if [ "$SSH_CONNECTION" ];then RSSH_CLIENTIP=$(echo $SSH_CONNECTION |awk '{ print $1}') RSSH_HOSTIP=$(echo $SSH_CONNECTION |awk '{ print $3}') else RSSH_CLIENTIP=$(who am i|awk '{print $5}' |sed 's/[()]//g') RSSH_HOSTIP=$(ip addr | grep inet| grep -v 127.0.0.1 | grep -v inet6 |grep -v virbr| head -n 1 | awk -F/ '{print $1}' | awk '{print $2}')fiRCMDLOG_FILE="/.cmdlog/cmdlog.$(date +%F)"[ -f $RCMDLOG_FILE -a -s $RCMDLOG_FILE ] || install -m 777 /dev/null $RCMDLOG_FILE 2>&-RLOGIN_TIMESTAMP=`date +%s`rsprompt_command() { RHISTCMD_PREV=$(history 1) if [ "$RHISTCMD_BEFORE_LAST" != "$RHISTCMD_PREV" ]; then { date "+%F %T ### ${HOSTNAME} ### ${USER} ### ${RSSHTTY} ### ${RSSH_CLIENTIP} ### ${RSSH_HOSTIP} ### ${SSH_CONNECTION} ### ${PWD} ### $RLOGIN_TIMESTAMP ###$(history 1|awk "{\$1=\"\";print}")"; } 2>&- >> $RCMDLOG_FILE fi RHISTCMD_BEFORE_LAST=$RHISTCMD_PREV}declare -r PROMPT_COMMAND='rsprompt_command'### end ###
注意:上述方法为判断当前操作获取到的操作内容和上次操作是否一样,若一样,说明不是新操作指令,则不记录;
上述优化后的版本运行一段时间后,又又又发现了新的问题:发现在每次新客户端连接时,会自动重复记录上次退出之前的最后一条操作指令。优化思路:日志中会记录每次客户端连接时的时间,如果发现指令操作时间比登录时间早,则说明此条指令记录是之前的,不予记录到日志中。### written by Jepson ###mkdir -m 777 -p /.cmdlog 2>&-declare -r HISTTIMEFORMAT='%F %T ### 'declare -r HISTCONTROL=''RSSHTTY=$(who am i |awk '{print $2}')if [ "$SSH_CONNECTION" ];then RSSH_CLIENTIP=$(echo $SSH_CONNECTION |awk '{ print $1}') RSSH_HOSTIP=$(echo $SSH_CONNECTION |awk '{ print $3}') else RSSH_CLIENTIP=$(who am i|awk '{print $5}' |sed 's/[()]//g') RSSH_HOSTIP=$(ip addr | grep inet| grep -v 127.0.0.1 | grep -v inet6 |grep -v virbr| head -n 1 | awk -F/ '{print $1}' | awk '{print $2}')fiRCMDLOG_FILE="/.cmdlog/cmdlog.$(date +%F)"[ -f $RCMDLOG_FILE -a -s $RCMDLOG_FILE ] || install -m 777 /dev/null $RCMDLOG_FILE 2>&-RLOGIN_TIMESTAMP=`date +%s`rsprompt_command() { RHISTCMD_PREV=$(history 1);RACTIONDATE=$(history 1|awk '{print $2" "$3}');RACTIONTIME=$(date -d "$RACTIONDATE" +%s) if [ "$RHISTCMD_BEFORE_LAST" != "$RHISTCMD_PREV" ] && [ "$RACTIONTIME" -ge "$RLOGIN_TIMESTAMP" ]; then { date "+%F %T ### ${HOSTNAME} ### ${USER} ### ${RSSHTTY} ### ${RSSH_CLIENTIP} ### ${RSSH_HOSTIP} ### ${SSH_CONNECTION} ### ${PWD} ### $RLOGIN_TIMESTAMP ###$(history 1|awk "{\$1=\"\";print}")"; } 2>&- >> $RCMDLOG_FILE fi RHISTCMD_BEFORE_LAST=$RHISTCMD_PREV}declare -r PROMPT_COMMAND='rsprompt_command'### end ###
本文完,有需要采集linux主机操作记录日志的,可以参考最后一个版本。如果本文对你有帮助,欢迎 关注、点赞、分享、推荐~
点关注,一起成长!