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

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

系统为:CentOS 7.9shell为默认的bash

centos 7.9中,historybash的内置命令,用于显示和管理历史记录的命令,但是在一些特殊的条件下会导致历史命令丢失,比如:

  • 非正常退出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_COMMANDbash中一个特殊的环境变量,用于定义每个命令提示符显示之前需要执行的操作,例如:

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

相关推荐
hjjdebug2 分钟前
linux 下 signal() 函数的用法,信号类型在哪里定义的?
linux·signal
其乐无涯3 分钟前
服务器技术(一)--Linux基础入门
linux·运维·服务器
Diamond技术流4 分钟前
从0开始学习Linux——网络配置
linux·运维·网络·学习·安全·centos
斑布斑布7 分钟前
【linux学习2】linux基本命令行操作总结
linux·运维·服务器·学习
Spring_java_gg17 分钟前
如何抵御 Linux 服务器黑客威胁和攻击
linux·服务器·网络·安全·web安全
✿ ༺ ོIT技术༻18 分钟前
Linux:认识文件系统
linux·运维·服务器
会掉头发1 小时前
Linux进程通信之共享内存
linux·运维·共享内存·进程通信
我言秋日胜春朝★1 小时前
【Linux】冯诺依曼体系、再谈操作系统
linux·运维·服务器
饮啦冰美式1 小时前
22.04Ubuntu---ROS2使用rclcpp编写节点
linux·运维·ubuntu