Skip to content

Go 主机安全面试:EDR Agent 自保护与资源控制

主机安全 Agent 跑在客户机器上,既要采集攻击证据,也不能把业务机器拖慢。面试里经常会问:Agent 被攻击者停掉怎么办?CPU、内存、磁盘、网络怎么限?线上误伤业务怎么排查?Go 研发要能把自保护、稳定性和可观测性讲成一条工程链路。

岗位场景

text
主机安全 Agent
  -> 采集进程 / 文件 / 网络 / 系统调用事件
  -> 本地缓存与规则预处理
  -> 上报服务端
  -> 遭遇停进程 / 删文件 / 改配置 / 资源打满
  -> 自保护、限流、降级、恢复和审计

Agent 自保护不是“永远杀不掉”,而是在权限允许的范围内提高破坏成本,并且把异常状态及时暴露出来。

高频面试题

1. EDR/HIDS Agent 为什么需要自保护?

简答:攻击者拿到主机权限后,常会先停掉安全 Agent、删除规则或阻断上报;自保护用于提升对抗成本和保留审计证据。

关键知识点:

  • 常见破坏动作包括 kill 进程、禁用服务、删除二进制、修改配置、清空日志、阻断网络上报。
  • 自保护能力受操作系统权限模型限制,用户态 Agent 不能承诺绝对不可杀。
  • 自保护要和审计、恢复、告警联动,否则只是“被打了但没人知道”。
  • 过强的自保护可能影响客户运维,需要提供授权关闭或维护窗口。

Go 落地思路:

  • 进程启动时记录 Agent 版本、配置版本、启动参数和运行身份。
  • 关键配置做签名或 hash 校验,发现篡改后进入降级或拒绝加载。
  • 自保护事件也要标准化上报,例如 agent_process_killedconfig_tamperedservice_disabled

2. Linux 上如何发现 Agent 被停止或服务被禁用?

简答:用看护进程、systemd 状态、心跳和服务端失联检测组合判断,单靠本地一个进程不可靠。

典型链路:

text
Agent 主进程
  -> 本地 watchdog 检查进程和配置
  -> systemd Restart 尝试拉起
  -> 服务端心跳检查主机是否失联
  -> 失联或异常重启次数过多时告警

关键知识点:

  • systemdRestart=always 可以恢复异常退出,但挡不住有权限的禁用操作。
  • watchdog 和主进程互相检查能发现意外退出,但不能替代服务端心跳。
  • 服务端要区分主机关机、网络故障、Agent 被停和租户主动卸载。

Go 落地思路:

  • 心跳字段包含 host_idagent_versionboot_timestart_timeconfig_version
  • 本地记录最近退出原因和重启次数,避免只看到“现在在线”。
  • 不要让 watchdog 写复杂业务规则,它只做存活检查和拉起。

3. 如何防止配置或规则被本地篡改?

简答:配置要有完整性校验和来源校验,加载失败要可观测,不能静默使用可疑配置。

关键知识点:

  • 本地规则、白名单、采集开关都可能被攻击者修改。
  • 只做文件权限不够,提权后的攻击者仍可能改文件。
  • 签名校验能确认配置来自可信发布方,hash 只能确认内容有没有变。
  • 配置回滚要保留版本号,避免被降级到旧漏洞版本。

Go 落地思路:

go
func SameSHA256(data []byte, expected string) bool {
	sum := sha256.Sum256(data)
	return hex.EncodeToString(sum[:]) == expected
}

实现要点:

  • 配置结构里带 versionissued_atexpires_atsignature
  • 校验失败时继续使用上一个可信配置,并上报篡改事件。
  • 配置加载逻辑保持小而可测,不要把规则执行和文件读取揉在一起。

4. Agent 如何控制 CPU 和内存占用?

简答:采集、解析、规则匹配、上报都要有预算,队列满时按风险降级,而不是无限堆内存。

关键知识点:

  • 高频事件包括进程创建、文件写入、网络连接和系统调用。
  • 无界 channel、无上限缓存和大对象常驻会导致内存上涨。
  • 正则、JSON 编解码、hash 文件、procfs 扫描都可能成为热点。
  • 客户更关心 p95/p99 开销,而不是平均值。

Go 落地思路:

  • 所有事件队列设置容量,并统计丢弃量。
  • 对低风险重复事件做采样或合并,对高风险事件保留完整证据。
  • context.Context 给单轮扫描和上报设置超时。
  • 线上问题先看 CPU profile、heap profile、队列长度和 goroutine 数。

5. 文件 hash、procfs 扫描这类慢操作怎么做降级?

简答:先采基础事件,再异步补证据;慢操作超过预算就跳过并记录状态。

处理流程:

text
原始事件入队
  -> 快速字段标准化
  -> 低成本规则预筛
  -> 可疑事件进入证据补全
  -> hash / procfs / 网络归因超时则标记 partial

关键知识点:

  • WebShell 小文件适合算 hash,但日志、压缩包、大型缓存不适合同步 hash。
  • /proc 读取可能失败,因为进程已经退出或权限不足。
  • 降级不是丢事件,而是明确告诉服务端证据不完整。

Go 落地思路:

  • 为 hash worker 设置并发上限和文件大小上限。
  • 证据结构增加 status 字段,例如 oktimeoutpermission_deniednot_found
  • 规则评分时把证据缺失作为不确定因素处理,避免误判。

6. Agent 上报失败时怎么处理?

简答:短时间失败可以本地缓存,长时间失败要限磁盘、压缩重复事件,并把失联风险交给服务端判断。

关键知识点:

  • 客户网络可能断开,服务端也可能限流。
  • 本地缓存不能无限增长,否则安全 Agent 会变成磁盘风险。
  • 事件要能去重和分级,优先保留高风险攻击链证据。
  • 重试要退避,避免网络恢复后瞬间打爆服务端。

Go 落地思路:

  • 本地 spool 目录设置总大小、单文件大小和保留时间。
  • 上报失败按批次记录错误码和下一次重试时间。
  • 缓存满时优先丢弃低风险重复心跳或统计事件,保留高风险告警。

7. 客户反馈 Agent 占用高,Go 研发怎么定位?

简答:先确认版本、主机角色和指标,再用 profile、日志和事件分布定位是哪条采集链路放大。

排查顺序:

text
客户问题
  -> 确认 Agent 版本 / 配置版本 / 主机角色
  -> 查看 CPU / 内存 / 磁盘 / 网络曲线
  -> 按事件类型统计采集量和丢弃量
  -> 查看队列长度、goroutine、profile
  -> 判断是采集源、规则、上报还是缓存问题

Go 落地思路:

  • 默认暴露轻量运行指标,至少包括事件速率、队列长度、丢弃数、上报延迟。
  • debug profile 要有权限控制,不能直接暴露给任意本地用户。
  • 修复后用问题样本或压测回放验证,避免只改一个阈值。

8. 自保护如何避免影响客户正常运维?

简答:自保护必须可审计、可授权、可回滚,不能把客户管理员也当攻击者一刀切。

关键知识点:

  • 客户可能需要升级、卸载、迁移、批量变更配置。
  • 安全产品如果无法维护,会被客户直接禁用。
  • 自保护策略要区分攻击行为、管理员操作和厂商升级流程。
  • 维护窗口和临时授权要有过期时间。

Go 落地思路:

  • 支持服务端下发临时维护令牌,带过期时间和操作范围。
  • 所有关闭保护、卸载、配置变更都写审计事件。
  • 本地只校验令牌和执行动作,不做复杂审批流。

学习要点

  • 自保护的核心是提高破坏成本、保留审计证据和及时恢复,不是承诺绝对不可杀。
  • Go Agent 要把采集、缓冲、规则、上报、watchdog 分开,避免单点卡死拖垮全链路。
  • 资源控制优先做有界队列、超时、并发上限、缓存上限和可观测指标。
  • 线上问题定位要从真实指标和 profile 出发,少猜,多看证据。

小练习

  1. 设计一个 AgentHeartbeat 结构体,字段要能区分重启、配置变更和主机重启。
  2. 给本地事件缓存设计 3 个限制:总大小、保留时间、低风险事件丢弃策略。
  3. 写一个配置加载流程:新配置校验失败时继续使用上一版可信配置,并输出审计事件。
  4. 思考一个客户维护场景:管理员要临时卸载 Agent,如何让操作可授权、可过期、可追踪?
最近更新