Go程序通过pty劫持实现终端审计日志,核心是fork子进程+Openpty接管stdin/stdout/stderr,记录UID、时间戳、命令、退出码及输出,采用结构化JSON行日志并设O_SYNC保障可靠性,避免像素级录屏。

Go 程序如何捕获终端操作并生成审计日志
Go 本身不提供终端录屏能力,但可通过拦截标准输入输出、结合 pty(伪终端)和进程控制实现操作行为的结构化审计记录。关键不是“录屏”,而是捕获命令执行全过程:谁(UID/GID)、何时(时间戳)、在哪(TTY/Shell)、执行了什么(命令行+参数)、返回结果(exit code + stdout/stderr)。真实生产环境里,直接调用 script 命令或复用 golang.org/x/sys/unix 的 ioctl 创建 pty 是最稳妥路径。
常见错误是试图用 os.Stdin.Read 直接读取用户键入——这会丢失 shell 自己处理的行编辑(如退格、上下箭头)、无法捕获命令实际执行环境,且绕过 shell 的 job control。
- 必须 fork 一个子进程,并用
unix.IoctlSetInt配合unix.IOC_IN设置 pty 主从关系 - 主端(Go 进程)读取从端输出时,需按行或按帧解析,避免粘包;建议用
bufio.Scanner配合Split(bufio.ScanLines) - 记录命令前,先读取
os.Getenv("SHELL")和os.Getpid(),再通过/proc/[pid]/stat反查父进程确认是否为交互式 shell - 敏感命令(如
sudo、ssh)可额外触发异步审计上报,但注意避免阻塞主 IO 流
用 golang.org/x/sys/unix 实现 tty 会话劫持的关键步骤
所谓“劫持”不是 root 权限下的内核级 hook,而是以普通用户身份启动一个受控 shell,并把它的 stdin/stdout/stderr 全部接管。核心依赖 unix.ForkExec 和 unix.Openpty,而非 exec.Command —— 后者无法暴露底层 fd 控制权。
典型失败场景:调用 unix.Openpty 后未立即对 slave fd 调用 unix.IoctlSetInt(slaveFd, unix.TIOCSCTTY, 0),导致子 shell 无法获得控制终端,表现为 bash: cannot set terminal process group 错误。
-
unix.Openpty返回主从 pair,主端用于 Go 读写,从端传给子 shell 的argv[0](通常设为"-bash"或"-sh") - 子 shell 启动后,立刻向从端写入
stty -icanon -echo; reset避免原始控制字符干扰日志格式 - 所有从端输出应带毫秒级时间戳前缀,例如
[2024-05-22T14:23:18.123Z] $ ls -l,便于后续关联分析 - 若需支持多路复用(多个用户会话),每个 pty 必须独立 goroutine 处理,且主端 fd 需设为非阻塞(
unix.SetNonblock)防止死锁
审计日志结构设计与落盘可靠性保障
审计日志不是简单追加文本,必须满足可检索、防篡改、低延迟三原则。纯文本 os.OpenFile(..., os.O_APPEND|os.O_CREATE) 在高并发下易丢事件,且无校验机制。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
容易被忽略的是:Go 的 log 包默认不 flush,若进程 crash,最后几条日志就丢了。更糟的是,用 fmt.Fprintf 写文件却不检查 error 返回值,会导致静默失败。
- 每条日志必须含唯一 trace ID(如
uuid.NewString()),与 pty 会话生命周期绑定 - 使用
os.O_SYNC打开日志文件(或定期file.Sync()),代价是写入变慢,但审计不可妥协 - 推荐结构化 JSON 行日志:
{"ts":"2024-05-22T14:23:18.123Z","uid":1001,"cmd":["ls","-l"],"exit":0,"stdout_len":128,"stderr_len":0} - 日志目录权限必须设为
0700,文件设为0600,避免非 root 用户读取他人会话内容
为什么不要自己实现“屏幕录制”(即像素级录像)
Go 没有跨平台 framebuffer 访问能力,Linux 下需 /dev/fb0 + mmap,macOS 需 CGDisplayStreamCreate,Windows 需 Desktop Duplication API —— 全部要求管理员/root 权限,且与 GUI 环境强耦合。终端审计的本质是行为记录,不是视觉还原。
真实需求中,99% 的合规检查(等保、ISO27001)只要求命令级审计,而非画面回放。强行做像素录制不仅大幅增加磁盘占用(GB/小时),还会引入音视频编解码依赖,让部署复杂度指数上升。
如果真有“回放”诉求,正确做法是:用上述 pty 方案完整记录所有输入输出流,再用轻量工具(如 asciinema 的播放器)将文本流渲染为终端动画。这样既零权限依赖,又可精确跳转到任意命令执行点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










