可靠记录Linux所有shell指令需分层设计:基础层用PROMPT_COMMAND实时落盘(/etc/profile.d中配置history -a),系统层用auditd捕获execve调用(-a always,exit -F arch=b64/b32 -S execve -k cmd_log),并辅以权限管控与远程归档。

Linux 系统要可靠记录所有 shell 历史指令,不能只依赖默认的 .bash_history。它易被删除、不记录非交互式执行(如 ssh user@host 'cmd')、无法覆盖 su 切换或 exec 绕过等场景。真正可用的方案需分层设计:基础层用 PROMPT_COMMAND 实时落盘,系统层用 auditd 捕获 execve 调用,再辅以权限与日志管理。
用 PROMPT_COMMAND 实现用户级实时记录
该方法简单有效,适用于大多数交互式终端,重点在于“每次命令执行后立刻写入”,避免退出才保存导致丢失。
- 在
/etc/profile.d/audit_cmd.sh中添加以下内容(确保对所有用户生效):
export HISTFILE="/var/log/bash_history_$(whoami)"
export PROMPT_COMMAND='history -a'
chmod 600 /var/log/bash_history_*
- 每个用户历史独立存放,避免多终端覆盖;时间戳精确到秒;文件权限设为仅属主可读写,防篡改
- 注意:
PROMPT_COMMAND对sudo -i或su -启动的新 shell 仍有效,但若用户手动执行unset HISTFILE或history -c,则后续命令不会记录
用 auditd 实现内核级全链路捕获
这是唯一能覆盖所有执行路径(包括 cron、脚本内调用、su/sudo、非交互式 SSH)的方式,核心是监听 execve 系统调用并启用参数记录。
- 确认内核支持并开启 argv 记录:
auditctl -e 2 && auditctl -s | grep argv(输出应含
argv=1)- 添加双架构规则(x86_64 必须同时加 b64 和 b32):
-a always,exit -F arch=b32 -S execve -k cmd_log
- 规则写入
/etc/audit/rules.d/cmd.rules并重启服务:systemctl restart auditd - 查某用户全部命令:
ausearch -u username -m execve --raw | aureport -f -i
补充关键防护与增强项
单靠记录不够,必须防止绕过和抵赖。
- 禁用用户修改历史能力:在
/etc/profile中加入chattr +a ~/.bash_history(需 root 执行),或更彻底地设readonly HISTFILE(部分 shell 支持) - 记录登录来源 IP:将
who -u am i解析结果嵌入PROMPT_COMMAND日志行,例如:export PROMPT_COMMAND='echo "$(date "+%F %T") $(who -u am i | awk "{print \$1,\$2,\$5}") $(history 1 | { read x cmd; echo "$cmd"; })" >> /var/log/audit.log' - 远程集中归档:配置
rsyslog将/var/log/audit/audit.log和自定义 history 日志转发至 SIEM 或专用审计服务器 - 定期轮转与压缩:
logrotate配置中加入/var/log/bash_history_*和/var/log/audit.log,保留至少 180 天
为什么不用纯 history 方案?
因为存在三类硬性盲区:
-
会话切换失效:执行
su - john后,新 shell 的历史写入/home/john/.bash_history,原用户配置不生效 -
非交互式绕过:
ssh user@host 'rm -rf /tmp/*'不触发PROMPT_COMMAND,也不读取~/.bashrc -
主动清除无痕:用户运行
history -c && rm ~/.bash_history即可清空本地痕迹,auditd 日志因由内核生成且受 root 权限保护,无法被普通用户删除


















