Bash完整记录用户的历史命令

本篇文章所依赖的环境为:

系统为: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环境变量,不会导致用户非正常状态退出命令丢失的情况。

相关推荐
雯宝7 分钟前
mnt_init 函数
linux
律宏阔6 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔6 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴6 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千6 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei377 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei377 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei377 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW7 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW7 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux