Skip to content

Go 主机安全面试:eBPF 采集进程执行与网络连接

主机安全 Agent 经常要回答一个问题:哪个进程执行了什么命令,又连到了哪里。eBPF 很适合做这类低开销采集,但面试官通常不会只问“会不会写 eBPF”,而是会追问字段、性能、丢事件、误报和 Go 侧工程化。

岗位场景

text
主机行为
  -> execve 进程执行事件
  -> tcp/connect 网络外连事件
  -> Go Agent 读取 ring buffer
  -> 字段标准化与上下文补全
  -> 本地规则 / 服务端检测
  -> 告警、降噪、攻击链还原

目标不是采集越多越好,而是稳定拿到能解释攻击行为的证据:进程、父进程、命令行、用户、容器上下文、目标 IP/端口和时间关系。

高频面试题

1. 为什么 HIDS/EDR 会用 eBPF 采集 exec 和 connect?

简答:eBPF 能在内核关键路径上拿到事件,比轮询 /proc 更及时,也比侵入式内核模块更容易做安全约束。

关键知识点:

  • execve 能描述“进程启动了什么命令”。
  • connect 能描述“进程主动连向哪里”。
  • eBPF 程序受 verifier 约束,不能随意破坏内核。
  • 采集点常见选择包括 tracepoint、kprobe、LSM hook,不同内核版本可用能力不同。

Go 落地思路:

  • Go Agent 负责加载 eBPF 程序、读取事件、做字段标准化。
  • 内核态只做必要过滤和字段拷贝,复杂规则留给 Go 或服务端。
  • 先覆盖 exec/connect 两类高价值事件,不要一开始就采满所有系统调用。

2. exec 事件至少要采哪些字段?

简答:能把一个命令放回进程树和用户上下文里的字段都要采。

建议字段:

字段作用
pid / ppid还原父子进程
uid / gid判断执行身份
comm / argv判断命令和参数
cwd判断执行目录
container_id区分宿主机与容器
timestamp关联前后事件

Go 落地思路:

  • eBPF 里采基础字段,Go 侧再补 /proc/<pid>/cmdline、进程哈希等慢字段。
  • 字段标准化时保留原始值,避免规则排查时只剩加工后的结果。
  • argv 做长度上限,避免异常命令行拖垮 Agent。

3. connect 事件怎么和进程执行事件关联?

简答:用 pid、进程启动时间和事件时间窗口关联,不能只靠进程名。

通俗理解:

text
exec: nginx worker -> /bin/sh -c curl 1.2.3.4
  within 3s
connect: same pid/start_time -> 1.2.3.4:4444

关键知识点:

  • PID 会复用,长时间只按 PID 关联会误判。
  • 进程启动时间可以降低 PID 复用带来的串案。
  • DNS、代理、NAT 会让“命令里的域名”和“连接里的 IP”不完全一致。

Go 落地思路:

go
type ProcKey struct {
	PID       uint32
	StartNS   uint64
}
  • 本地维护一个小 TTL 缓存,保存最近进程上下文。
  • 网络事件到来时按 ProcKey 取上下文。
  • 缓存只保留近期事件,过期就删,避免常驻内存增长。

4. eBPF 采集会不会影响主机性能?怎么控制?

简答:会,所以内核态少做事、Go 侧限流、批量上报,并监控丢事件。

性能风险:

  • 每次系统调用都采集会放大 CPU 消耗。
  • ring buffer 堵塞会丢事件。
  • 过多字符串拷贝会增加内核态成本。
  • Go 侧 JSON 编码、网络上报也可能成为瓶颈。

Go 落地思路:

  • 内核态只过滤明显无用事件,例如忽略 Agent 自身 PID。
  • Go 侧使用固定大小 channel,满了要计数,不要无限堆内存。
  • 上报按批次发送,失败时退避重试。
  • 暴露 events_totalevents_lostqueue_len 这类指标。

5. 如何降低 exec/connect 规则的误报?

简答:不要单点命中,至少用进程上下文、网络目标和行为序列组合判断。

例子:

单点信号更可靠的组合
进程名是 shWeb 进程子进程 + shell + 外连
连接高危端口可疑命令 + 新进程 + 外连
命令包含 curl非交互服务账号 + 下载可执行文件

Go 落地思路:

  • 规则结果要带命中原因,方便客户和安全运营复核。
  • 对常见运维脚本做白名单,但白名单要带范围:路径、用户、父进程、主机组。
  • 降噪优先做去重和聚合,不要直接丢弃原始证据。

6. eBPF 程序加载失败时怎么办?

简答:要有可解释降级,不能让 Agent 直接失明。

常见失败原因:

  • 内核版本太低。
  • BTF 不可用或字段布局不兼容。
  • 权限不足,缺少 CAP_BPFCAP_PERFMON 或等价能力。
  • verifier 拒绝程序加载。

Go 落地思路:

  • 启动时输出明确错误:内核版本、加载点、verifier 日志摘要。
  • 能降级时用 auditd、netlink、/proc 轮询补一部分能力。
  • 降级状态要上报服务端,让规则知道当前数据质量。

7. ring buffer 丢事件后检测还能可信吗?

简答:可信度要下降,告警和排障都要看到丢事件指标。

关键知识点:

  • 丢 exec 事件会导致网络事件缺少进程上下文。
  • 丢 connect 事件会导致攻击链断裂。
  • 丢事件不是偶发日志问题,而是检测证据缺口。

Go 落地思路:

  • 单独记录丢事件计数和时间段。
  • 告警里标记 data_quality: degraded
  • 当丢事件持续升高时,优先限流低价值事件,而不是让关键事件一起丢。

8. Go Agent 如何避免自己被采集规则误伤?

简答:Agent 自身行为要可识别、可过滤、可审计。

关键知识点:

  • Agent 会读 /proc、打开 eBPF map、连接服务端,这些行为容易像扫描或外连。
  • 不能粗暴忽略所有同名进程,攻击者可能伪装进程名。
  • 过滤条件应包含进程路径、签名、启动参数、父进程和安装目录。

Go 落地思路:

  • 启动后记录自己的 PID、可执行路径和 hash。
  • 内核态可按 PID 跳过高频自采集,Go 侧仍保留关键审计日志。
  • 检测规则里给 Agent 行为单独建画像,不和业务进程混在一起。

学习要点

  • eBPF 适合做高价值、低开销的主机事件采集,但不是万能采集器。
  • exec 和 connect 的核心价值在于能还原“命令执行 -> 网络外连”的行为链。
  • PID 关联要考虑复用,最好带进程启动时间。
  • 性能控制要从内核态、Go 队列、上报链路一起看。
  • 误报治理靠上下文和行为序列,不靠单个关键字。

小练习

  1. 设计一个 ExecEventConnectEvent 的 Go 结构体,字段要能支持攻击链还原。
  2. 写一个 TTL 缓存,把最近 5 分钟的 exec 事件和 connect 事件按进程关联起来。
  3. 想一个误报场景:正常运维脚本执行 curl 外连,如何用父进程、用户、路径和主机组降低误报?
  4. 如果线上发现 events_lost 持续升高,你会先看 CPU、队列、上报延迟还是规则量?为什么?
最近更新