本篇文章所依赖的环境为:
系统为:
CentOS 7.9
,shell
为默认的bash
在centos 7.9
中,history
为bash
的内置命令,用于显示和管理历史记录的命令,但是在一些特殊的条件下会导致历史命令丢失,比如:
- 非正常退出
shell
- 在退出
shell
前命令未保存到历史文件中
为了避免如上情况,又想要记录用户所执行的命令,例如:作为安全审计等。
针对上述问题,可以使用2种方法来解决:
- 修改
HISTFILE
环境变量内容来存储日志。 - 使用
PROMPT_COMMAND
环境变量来输出历史命令进行存储日志。
history相关的环境变量
在介绍如何保存历史命令之前,需要先了解history
的环境变量,在使用histroy
命令中,会涉及到一系列相关的环境变量,我们一般将其写到/etc/profile
或者是用户的bashrc
文件中,即:开机会加载的文件以便重新定义环境变量。
HISTFILE
: 历史命令记录的存储文件路径
HISTFILE
环境变量保存的是 history
命令历史记录 的文件存储路径,若用户为root
,则默认为/root/.bash_history
,可以通过设置该环境变量来改变该路径。默认情况下,当用户登录机器的时候,history
会加载该文件的内容作为历史命令。
HISTFILESIZE
: 历史命令的文件最大行数
HISTFILESIZE
定义的是历史文件中的最大行数,centos 7
系统的默认值为1000,当超过该值后,会迭代删除文件中最早的命令。
HISTSIZE
: shell
历史会话中的最大行数
HISTSIZE
定义的是在shell
会话中的最大行数,默认为1000,当超过该值后,会迭代删除会话中最早的命令。
HISTTIMEFORMAT
:控制历史命令显示的时间格式
HISTTIMEFORMAT
定义的是历史命令显示的时间格式,默认为空,可以设置为date
命令所支持的时间变量,例如: %F %T
所展示的效果为
如何保存历史命令
简单根据用户记录历史命令
通过上述history
相关环境变量,可以通过根据不同的登录用户从而设置不同的HISTFILE
进行历史记录保存,例如修改/etc/profile
文件内容,追加如下内容:
bash
# tail /etc/profile
HISTTIMEFORMAT="%F %T "
USERID=$(id -u)
HISTFILE="/var/log/${USER}_${USERID}.log"
touch $HISTFILE
chmod 777 $HISTFILE
...
#
上述语句表示的含义是将历史命令显示的时间格式修改为YYYY-mm-dd HH:MM:SS
的形式,并且定义历史记录的文件存储路径为/var/log/用户名_用户ID.log
,并且创建文件赋777
权限。
这样的话,在登录机器后,会在/var/log
下产生历史文件的日志,例如:
除此之外,你还可以使用更加细分的逻辑来存储历史命令,比如:根据用户以及来访者的IP地址进行命令存储,只需要修改其环境变量即可,例如修改/etc/profile
文件内容:
bash
# tail -n 14 /etc/profile
HISTTIMEFORMAT="%F %T "
USERID=$(id -u)
if [ -z "$SSH_CONNECTION" ];then
CLIENT="localhost"
else
CLIENT=$(echo $SSH_CONNECTION | awk '{print $1}')
fi
HISTFILE="/var/log/${CLIENT}_${USER}_${USERID}.log"
touch $HISTFILE
chmod 777 $HISTFILE
...
#
这样存储的日志会更加详细,具体路径为:/var/log/客户端IP_用户名_用户ID.log
,如果不是通过ssh
登录的,就使用localhost
代替客户端IP。
这样的好处有很多,可以更加细分的存储日志,查找起来也非常方便,但是也有问题,比如:下一次客户端IP变了后,再进行history
查找历史命令,就无法找到此前的命令了,因为HISTFILE
存储路径改变了。
使用PROMPT_COMMAND
来存储历史命令
PROMPT_COMMAND
是bash
中一个特殊的环境变量,用于定义每个命令提示符显示之前需要执行的操作,例如:
上诉命令将PROMPT_COMMAND
设置为pwd
,即打印当前路径,所以而后敲击的每条命令结束后,再下一个提示符显示之前都会执行该操作,可以利用此来记录历史命令。
在bash
中可以直接使用history n
来打印最后的n
条记录,比如
基于上述,可以在/etc/profile
中基于PROMPT_COMMAND
来调用存储信息,一般而言可以调用一个钩子,例如:
bash
# tail -n 3 /etc/profile
HISTTIMEFORMAT="%F %T "
PROMPT_COMMAND='bash /usr/local/bin/historyTracker "$(history 1 | { read x; echo $x; })"'
#
其中/usr/local/bin/historyTracker
是我们自定义的钩子,可以是shell
脚本,也可以是python
脚本等等,注意调用的方式不同,最重要的是后面的钩子参数是跟的是最后的命令,至于为什么是 $(history 1 | { read x; echo $x; })
,这是因为直接使用在PROMPT_COMMAND
的时候,调用history
命令是在一个新的shell中执行的,会取到空的值,当我们使用{}
命令块的时候,会创建一个子shell
,这样就避免了该问题。
接下来只需要编辑钩子脚本的内容即可完成了,例如:
bash
# cat /usr/local/bin/historyTracker
#!/bin/bash
# 记录历史记录至文件
TIMEDAY=$(date +"%F")
USERID=$(id -u)
if [ -z "$SSH_CONNECTION" ];then
CLIENT="localhost"
else
CLIENT=$(echo $SSH_CONNECTION | awk '{print $1}')
fi
readonly HISTORYFILENAME="/var/log/history_${CLIENT}_${USERID}_${TIMEDAY}.log"
echo $* >> $HISTORYFILENAME
#
注意钩子脚本的权限,当然你也可以直接将上诉代码写在/etc/profile
中,这样的话,会显得非常凌乱。
使用PROMPT_COMMAND
的方式来记录操作记录,不会去修改已有的history
环境变量,不会导致用户非正常状态退出命令丢失的情况。