Skip to content

Go 主机安全面试:容器进程识别与逃逸检测

云上业务大量跑在容器里,主机安全 Agent 不能只把所有行为都当成普通 Linux 进程。面试官常会追问:如何判断一个进程属于哪个容器?容器逃逸有哪些证据?Go Agent 怎样低开销采集、标准化、关联和降噪?

岗位场景

text
Linux 主机 / 容器节点
  -> 采集进程、命名空间、cgroup、挂载、网络和文件事件
  -> 识别容器 ID、镜像、Pod、宿主机进程边界
  -> 关联异常权限、敏感挂载、宿主机文件访问、可疑外联
  -> 判断容器逃逸、越权访问或高危配置
  -> 输出可解释告警和排查证据

高频面试题

1. Go Agent 如何判断一个进程是不是容器进程?

简洁答案:优先看 /proc/<pid>/cgroup/proc/<pid>/mountinfo、namespace inode 和容器运行时元数据,单个字段不可靠,要组合判断。

关键知识点:

  • cgroup 路径里常见 docker、containerd、kubepods、cri-containerd 等标识。
  • namespace inode 可用于判断进程是否和宿主机共享 pid、mnt、net 等命名空间。
  • 容器 ID 可能被截断或格式不同,不能只按固定路径切字符串。

Go 落地思路:

  • os.ReadFile 读取 /proc/<pid>/cgroup,按行解析 controller 和 path。
  • 缓存 pid -> containerID,设置短 TTL,避免每个事件都扫 procfs。
  • 解析失败时返回“未知容器上下文”,不要直接丢事件。
go
func likelyContainerCgroup(path string) bool {
	return strings.Contains(path, "kubepods") ||
		strings.Contains(path, "docker") ||
		strings.Contains(path, "containerd")
}

2. 为什么容器逃逸检测不能只看“容器内执行了 shell”?

简洁答案:容器里执行 shell 很常见,真正高危的是从容器边界访问宿主机资源、提权能力或敏感控制面。

关键知识点:

  • 运维、构建、调试场景都会产生 /bin/shbashbusybox
  • 逃逸风险通常伴随 privileged、hostPID、hostNetwork、危险 capabilities、宿主机目录挂载。
  • 单点行为容易误报,要看上下文和后续链路。

Go 落地思路:

  • 事件模型里保留 container.idnamespacecapabilitiesmountsparent_process
  • 规则先识别“高风险容器上下文”,再匹配 shell、敏感文件访问和外联。
  • 告警证据展示“为什么这个 shell 不正常”,而不是只输出命令名。

3. 容器访问宿主机敏感文件时,应该采集哪些证据?

简洁答案:至少采集进程身份、容器身份、访问路径、真实挂载来源、操作类型、父进程和时间窗口内的相关命令。

关键知识点:

  • 容器内路径和宿主机真实路径可能不同,需要结合 mountinfo 还原。
  • /host/etc/shadow/var/run/docker.sock/proc/sys/kernel/* 这类路径风险较高。
  • 文件访问本身不一定恶意,备份、监控和安全产品也可能访问敏感路径。

Go 落地思路:

  • 文件事件中同时记录 pathmount_sourcecontainer_idprocess_uid
  • docker.sock、宿主机根目录挂载、Kubernetes token 做高优先级规则。
  • 引入客户可配置白名单,但白名单必须带作用范围和过期时间。

4. 如何检测容器里滥用 Docker Socket?

简洁答案:监控容器进程访问 /var/run/docker.sock 后调用创建容器、挂载宿主机目录、设置 privileged 或执行宿主机命令的行为。

关键知识点:

  • 挂载 Docker Socket 等于把容器运行时控制权暴露给容器内进程。
  • 攻击者可通过创建特权容器挂载 / 实现宿主机文件访问。
  • 仅发现 socket 被打开还不够,最好关联 HTTP API 请求或后续容器创建事件。

Go 落地思路:

  • 文件层采集 socket open/connect,网络或审计层补充本地 Unix socket 行为。
  • 服务端或本地规则关联“访问 socket -> 创建特权容器 -> 访问宿主机路径”。
  • 对 CI/CD runner 等合法场景做基线,避免一刀切误报。

5. 容器逃逸规则如何降低误报?

简洁答案:把“危险配置”和“危险行为”组合评分,并用镜像、命名空间、账号、路径、时间窗口和历史基线降噪。

关键知识点:

  • privileged=true 是风险,不等于已经攻击。
  • 安全扫描器、日志采集器、备份 Agent 可能天然拥有高权限。
  • 误报治理要保留证据,不能把高危行为简单静默。

Go 落地思路:

  • 用结构化字段做规则输入,避免规则直接依赖原始日志文本。
  • 同一个容器短时间重复命中时做聚合,告警里展示代表性证据。
  • 对白名单命中也记录审计事件,便于客户复盘。

6. Go Agent 采集容器上下文时如何控制性能?

简洁答案:事件驱动优先,procfs 扫描兜底;容器元数据做缓存,字段补全异步化,队列满时保留高价值事件。

关键知识点:

  • 全量扫描 /proc、解析 mountinfo、查 runtime metadata 都有成本。
  • 容器创建和退出频率高时,缓存过期策略会影响准确性。
  • 安全事件不能无限堆积,需要背压和降级策略。

Go 落地思路:

  • containerID -> metadata 建短 TTL 缓存,miss 时异步补全。
  • 高危事件同步补关键字段,低危事件允许后补或服务端补全。
  • 用指标观测缓存命中率、解析耗时、队列长度和丢弃数量。

7. 线上客户反馈“容器逃逸告警误报”,你怎么定位?

简洁答案:先还原告警证据链,再确认容器配置、镜像身份、进程树、挂载关系、规则命中字段和白名单范围。

关键知识点:

  • 客户现场最怕只给“命中规则”不给证据。
  • 误报可能来自字段解析错、容器 ID 关联错、历史基线缺失或规则阈值过低。
  • 定位过程要能复现,最好用脱敏事件回放。

Go 落地思路:

  • 告警中保留规则版本、事件 ID、关键字段快照和关联窗口。
  • 提供离线 replay 工具,用同一批事件验证规则调整前后差异。
  • 修复时优先改共享解析或规则条件,不在单个客户分支里写特殊逻辑。

学习要点

模块需要掌握的点
Linux 基础cgroup、namespace、mountinfo、procfs、Unix socket
容器运行时Docker、containerd、CRI、Kubernetes Pod 与容器 ID
检测规则危险配置、敏感挂载、Docker Socket、宿主机路径访问
Go 工程procfs 解析、缓存、事件标准化、异步补全、背压控制
误报治理基线、白名单、证据链、离线回放、规则灰度

小练习

  1. 写一个函数读取 /proc/<pid>/cgroup,提取可能的 container ID,并说明哪些格式会解析失败。
  2. 设计一条规则:容器进程访问 /var/run/docker.sock 后创建 privileged 容器,输出需要的字段。
  3. 复盘一个误报场景:CI runner 合法使用 Docker Socket 时,怎样保留风险提示但降低告警噪声?
最近更新