
1. 先复现原文配置PROMPT_COMMAND 到底设成了什么1.1 登录 shell 的 client.sh 是否被正常加载很多人把用户行为审计脚本放在 /etc/profile.d/client.sh然后默认它一定会生效。实际上这个目录下的脚本只在登录式 shell 启动时被 /etc/profile 读取。你通过 SSH 登录时Shell 会加载这些脚本但如果你在图形界面里打开终端或者从 tmux/screen 里新建会话当前 Shell 多半不是登录式PROMPT_COMMAND 这个变量根本不会被赋值cmd_track.log 自然一条命令都记不到。先用下面这条命令确认变量是否已经在当前会话里echo $PROMPT_COMMAND如果输出为空再用登录式 Shell 检查一次bash -l -c echo $PROMPT_COMMAND如果你在登录式会话里能看到完整的 PROMPT_COMMAND而普通终端里看不到说明问题不是 rsyslog而是 Shell 加载范围不对。解决办法有两种要么让客户端连接时走登录式 Shell要么把变量定义挪到 /etc/bashrc 里。注意/etc/bashrc 会在每个交互式 Shell 启动时加载影响面更大。除了加载范围还要检查 /etc/profile.d/client.sh 是否具备执行权限以及文件内容在语法上是否完整。用bash -n /etc/profile.d/client.sh可以快速校验如果没有任何输出说明语法没断裂。很多时候脚本里某个引号没闭合会让整段配置在 source 时直接报错变量根本没被导出。1.2 原文中的反引号与单引号嵌套一个经典坑原文的 PROMPT_COMMAND 长这样我把反引号换成了更稳妥的$()写法但保留了原逻辑export PROMPT_COMMAND{ msg$(history 1 | { read x y; echo $y; }); logger -p local4.info [LOCAL|$(grep IPADDR /etc/sysconfig/network-scripts/ifcfg-eth0 2/dev/null | sed s/IPADDR//g)] [SSH|${SSH_CONNECTION} ${SSH_TTY}] [USER|${USER}] [PWD|${PWD}] $msg; }如果你照抄的是原文那种反引号加单引号混排的写法最容易踩两个坑。第一外层如果用了双引号做赋值那么$SSH_CONNECTION、$USER、$PWD会在赋值那一刻就被展开成空字符串等提示符真正出现时logger 拿到的是一堆空字段虽然日志文件会生成但内容没法看。第二如果外层单引号内部又出现了单引号Shell 会把第一个内层单引号当作字符串结束标志于是整条 PROMPT_COMMAND 被截成半句话看起来设置了变量实际执行时直接语法错误。所以在排障时先执行echo $PROMPT_COMMAND看输出是否是一条完整命令。重点看 LOCAL 后面有没有 sed 片段以及变量名有没有变成实际 IP 或当前路径。如果已经被展开说明赋值时引号用错了需要重写成外层单引号、内层用双引号的形式。2. 把 Codex 指到 TaoToken让它在只读模式下帮你分析2.1 准备 Key 和 Base URL这种排障适合让 Codex 做“配置解释器”你把文件和状态输出丢给它它按链路逐项检查。要调用 Codex先要有一个模型通道。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册并创建 API Key本文统一用 YOUR_API_KEY 作为占位符。创建完成后接口地址是https://taotoken.net/api注意末尾不要加 /v1。这个地址只用于填进 Codex 这类工具不要在浏览器里直接打开当官网使用。TaoToken 在本次排障里只承担模型 API 通道的角色它不会去操作你的服务器所有诊断命令都要由你在本地终端执行再把输出贴回 Codex。2.2 修改 ~/.codex/config.tomlCodex CLI 通过 ~/.codex/config.toml 读取模型供应商配置。为了让 Codex 走 TaoToken可以新增一个 model_provider示例配置如下# ~/.codex/config.toml model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY然后设置环境变量export TAOTOKEN_API_KEYYOUR_API_KEY启动 Codex 时用-m指定模型 ID模型 ID 以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场当时的列表为准这里用 YOUR_MODEL_ID 占位codex -m YOUR_MODEL_ID启动后先问一个无关紧要的问题比如“11 等于几”确认 Base URL 和 Key 都能通。如果返回 401检查环境变量名是否和env_key完全一致如果提示 model not found回模型广场看模型 ID 是否带日期后缀或特殊前缀。这一步不是为了测性能而是为了确保后续排障对话不会因为通道问题中断。2.3 把三个关键输入原样贴给 Codex进入 Codex 交互界面后建议按顺序粘贴以下内容并说明你的目标第一段Linux 发行版和 bash 版本号例如 Ubuntu 22.04 bash 5.1。第二段/etc/profile.d/client.sh 的完整内容。第三段/etc/rsyslog.conf 中与 local4 相关的行。第四段systemctl status rsyslog的输出。然后追加一句限制只检查配置逻辑不要生成自动执行的脚本所有需要运行的命令列给我我自己在服务器上跑再把结果贴回给你。这样 Codex 就会按“变量是否设置 → logger 是否能写 → rsyslog 规则是否命中 → 服务是否健康”的顺序给你诊断清单。它不会直接连接服务器更不会替你执行任何命令。3. 按链路逐项核对local4.*、PROMPT_COMMAND、服务状态3.1 rsyslog.conf 里那条定向规则真的生效了吗先看规则写法。在 rsyslog.conf 中local4.* 与日志文件之间用 tab 或空格分隔均可但要注意行首不要有空格也不要写在模块加载段之前。一个典型的定向规则是这样local4.* /var/log/cmd_track.log要验证语法是否正确可以运行rsyslogd -N1如果输出config file ... is OK说明配置文件本身没问题。接着手动发一条测试日志logger -p local4.info manual test for cmd_track然后立刻查看ls -l /var/log/cmd_track.log tail -5 /var/log/cmd_track.log如果文件出现了说明 rsyslog 到文件的链路是通的问题回到 PROMPT_COMMAND 那一端。如果文件没出现再查 SELinuxgetenforceEnforcing 状态下rsyslog 尝试创建新日志文件时可能被 AVC 拒绝。用ausearch -m avc -ts recent查看是否有 denials如果有可以通过audit2allow -a -M rsyslog_cmd_track生成策略模块或者把这个日志目录加入例外。注意这些操作都需要在本地以 root 执行Codex 只负责解释输出。这里还有一个很多人忽略的点rsyslog 对/var/log/cmd_track.log的父目录要有写权限。如果 /var/log 被设置为只读或者日志文件被其他进程提前用错误的属主创建rsyslog 会静默写入失败。排查时可以临时把规则目标改到 /tmp/cmd_track.log再 logger 一次如果 /tmp 下能生成说明是目录权限问题而不是规则问题。3.2 514 端口监听为什么不重要原文在重启后习惯用netstat -aulntp | grep rsyslog确认监听。这里有个误区rsyslog 要写本地文件根本不需要监听 514 端口。只有当你用 rsyslog 接收远程日志、启用了 imudp 或 imtcp 模块时514 端口才会出现监听。所以排障时看到 514 没有监听不代表本地入库规则失败了。真正要关注的是systemctl status rsyslog里的Active: active (running)以及journalctl -u rsyslog有没有报错。如果你确实还需要远程日志接收确认 rsyslog.conf 里有没有这两行module(loadimudp) input(typeimudp port514)没有这两行netstat 永远不会显示 514。这不影响 local4.* 写入本地文件所以不要把时间花在启动网络模块上。3.3 PROMPT_COMMAND 里的 history 是否为空另一个隐蔽原因是history 1拿不到命令。这个问题多出现在非交互 Shell或者因为 HISTSIZE/HISTFILESIZE 被清空、HISTFILE 指向了不可写路径。可以在当前 Shell 里直接执行history 1如果没有任何输出PROMPT_COMMAND 里的 msg 就是空变量logger 虽然会执行但记录的是空消息。这种情况下日志文件本身还会生成所以不会导致“不落盘”但会导致“落了盘却看不到命令”属于同一类审计失效。要修复需要在 client.sh 顶部显式开启set -o history并且确认 HISTFILE 有可写权限。用ls -l ~/.bash_history看一下属主和权限如果文件被 root 占用普通用户登录时就会静默失败。4. 验证到底该看哪个文件、哪个权限4.1 从 logger 到文件手动触发一条真实记录当我们最终把问题定位在 PROMPT_COMMAND 后可以分两步验证修复结果。第一步先杀掉当前会话重新登录让 /etc/profile.d/client.sh 重新执行。第二步执行任意一个简单命令比如echo test然后去看tail -3 /var/log/cmd_track.log正常会看到类似这样的一行Jul 12 10:23:45 host [LOCAL|192.168.1.10 -- SSH|192.168.1.2 51724 -- USER|root -- PWD|/root]: echo test注意实际格式可能略有不同但至少包含命令体。文件权限应该是 600属主 root。如果文件存在但权限不是 600检查 rsyslog 的创建掩码配置$umask 0077是否被覆盖。另外如果每次提示符出现时日志写两行多半是 PROMPT_COMMAND 被赋值了两次比如 /etc/profile.d/client.sh 和 /etc/bashrc 各写了一次搜索所有包含 PROMPT_COMMAND 的文件逐个排除。4.2 在 TaoToken 控制台核对这次 Codex 调用配置和排障都做完后回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 可以看模型广场上的模型 ID 是否与你填的一致也可以到控制台确认刚才 Codex 发出的请求有没有被记录。如果控制台有调用记录说明 Base URL 和 Key 没问题如果没有任何记录问题不在 rsyslog而在 TaoToken 通道本身优先检查 Base URL 是否写成了带 /v1 的地址。注意控制台看到的用量和本次排障的会话数对应但不能代替本地日志的验证两者是独立链路。5. 一口地道的收尾把修复固定到 client.sh 里5.1 推荐一份加了保护的 client.sh结合上面的排障我建议把 /etc/profile.d/client.sh 写成下面这样。它同时处理了 history 未启用、命令体只取第一词、sed 引号冲突三个问题# /etc/profile.d/client.sh if [ -n $BASH_VERSION ]; then set -o history _cmd_track() { local msg msg$(HISTTIMEFORMAT history 1 | sed s/^ *[0-9]* *//) logger -p local4.info [LOCAL|$(grep IPADDR /etc/sysconfig/network-scripts/ifcfg-eth0 2/dev/null | sed s/IPADDR//g)] [SSH|${SSH_CONNECTION} ${SSH_TTY}] [USER|${USER}] [PWD|${PWD}] $msg } export PROMPT_COMMAND_cmd_track fi这里的 sed 在 msg 提取中把history 1输出的行号去掉保留整条命令。函数体里的$(...)是在每次提示符出现时求值不是赋值时求值因为外层 PROMPT_COMMAND 只是一个字符串_cmd_trackbash 每次执行它时会走到函数内部去。这个版本比原文更稳也更好读。注意如果服务器上存在多个网卡或没有 ifcfg-eth0 文件grep 可能取不到 IP。这时可以把 LOCAL 部分替换成hostname或直接去掉不要让它阻断整条 logger 命令。更保险的做法是给 grep 加2/dev/null并容忍空值这样即使没有 IP日志也照常写入。5.2 排障后的自检清单以后遇到“命令审计日志没写”的问题不要一上来就重启 rsyslog。按这个顺序查echo $PROMPT_COMMAND看变量有没有设置、有没有引号截断。logger -p local4.info test后看/var/log/cmd_track.log是否出现用于快速隔离是 rsyslog 端还是 bash 端的问题。rsyslogd -N1检查配置语法。getenforce加ausearch -m avc -ts recent查 SELinux。最后才检查systemctl status rsyslog和journalctl -u rsyslog。Codex 在 TaoToken 通道下可以做这个流程的“解释器”和“命令生成器”但每一步仍然由你在本地执行。这样既保留了审计日志的原始链路也把排查时间压下来。排障过程中产生的临时测试消息可以在确认正常后清空/var/log/cmd_track.log避免审计数据里混进无关内容。排障结束别忘了给这次 Codex 调用一个确认。可以打开 TaoToken 模型对话 看看同一把 Key 是否能在对话页正常返回再回到 Coding Plan 看剩余额度够不够日常写代码。如果还没创建 Key去 控制台 API Keys 补一把后面想配置其他工具时Base URL 仍然用 https://taotoken.net/api只是不要和官网页面地址搞混。