记录牧安平台从 0 到 1 的开发过程,一个待业在家的网安老兵边做边想。
聊了两期探针的事情了,这一期聊聊资产。
# 资产不是扫出来的,是确认出来的
探针跑稳了以后,有件事一直搁在我心里没底:我对自家网里到底有哪些资产,其实说不清楚。
最早我的想法特别朴素,跟刚入行时候学的一样——资产嘛,扫一遍不就知道了?nmap 一把梭,端口、服务、操作系统全出来,按开放状态标个"高危",一份清单甩出来,几百条,看着特别有成就感。
干了一阵才发现,那玩意儿是虚胖。
清单里一半是访客手机、临时虚拟机、打印机的 IP,还有一堆"端口开着但谁也说不清是干嘛"的影子。真要运营起来,对着几百条"未授权访问高危"发呆,根本看不过来。扫出来不等于管得住,更不等于"这是我的资产"。
这期就聊聊,我是怎么把"扫出来的一堆 IP"变成"能管、能盯、能整改的资产"的。这条线在 L3、L2、L1 三级上各管一段,踩的坑也不少。
探针怎么"看见"资产:先嗅,再探
三级探针(L3)是资产的第一道关,它蹲在客户内网的交换机镜像口上,用 scapy 旁路抓包。凡是见过通信的 IP 和 MAC,都会被记进一张 `probe_hosts` 表,也就是"待确认资产列表"。
这一步是被动发现,不动声色,不主动发包,所以不会惊动网里任何设备。前端页面上有个「待确认资产」区,流量跑着跑着,里面就慢慢长出来了——谁跟谁聊过天,一目了然。
但问题来了:嗅探到的,不能直接当资产。
原因很现实。镜像口看到的通信里,混着访客手机、临时虚机、一台谁都不知道的测试服务器、甚至隔壁部门的打印机。这些东西一旦被当成"资产"塞进台账,后面所有统计、所有告警都跟着脏。所以我在设计上钉了一条铁律:**流量识别出来的主机,绝不直接入库,必须有人拍板确认。**
光靠嗅探还不够细。原来探针的主动探测只做了 ICMP ping,也就是发个回声请求看回不回。可现在云主机、加固过的网络,很多是禁 ping 的,你 ping 它不理你,它就"不存在"了——其实是存在,只是不搭理你。
这个坑我踩过。后来改成 **ICMP + TCP SYN 双探测**:除了 ping,再对 22、80、443、445 这几个常见端口发一个 SYN 包,只要回 SYN-ACK,就认定它还活着。禁 ping 的环境也能捞出来。这等于给被动嗅探补了一层主动的网,漏检少了一大截。

确认即扫描:点一下,剩下的交给系统
早期最让我尴尬的一件事,是探针"会扫,但扫不出东西"。
翻代码的时候我自己都乐了:engine 里只会按端口开放状态标一句"未授权访问高危",真正的漏洞、弱口令、深度指纹,全是零命中。全代码里 `vuln`、`weak`、`nmap` 这几个词搜出来,干干净净。也就是说,我之前引以为傲的那份"资产清单",本质上是"端口开了的机器列表",虚得不行。
改造方向很明确:内置 scapy / nmap 依赖(Windows 版探针自带,包体是大了点,但值得),做真实的漏洞扫描、弱口令探测(SSH、RDP、MySQL 这些)、还有深度指纹(操作系统、服务、版本、组件)。
但光"能扫"还不够顺手。最早的流程是:人工确认入库 → 再手动点一次"扫描"。两步操作,人一多就乱,经常确认完忘了扫,台账里资产有了,漏洞却是空的。
所以我定了**"确认即扫描"**:操作员在页面上点一下"标记为资产",系统自动触发探活 + 指纹/漏洞扫描,不用再手动补一刀。确认这个动作,本身就是资产化的第一步,后面的活儿系统接着干。
另外,半夜让它自己跑这事儿也得安排上。我加了一张 `scan_plan` 表,可以设自动计划,默认窗口压在 **凌晨 2 点到 4 点**——这个时间点是特地错开的,因为复盘任务也是半夜跑(L3 零点、L2 两点、L1 四点),资产扫描不能跟复盘抢资源。首次跑全量建基线,之后只上行高危结果,流量也省了。

L2 怎么接住:确认闭环在客户这边
资产上行到二级平台(L2,客户本地SOC)之后,L2 干的是"汇聚 + 确认闭环 + 汇总上报"的中枢活儿。
L3 推过来的资产分两路走:已经人工确认过的(`confirmed=true`)直接进 `assets` 台账;还没确认的(`confirmed=false`)进 `asset_candidates`,也就是待确认暂存区。
注意,关键的"确认"动作,是在 L2 这端完成的,不是 L1。
L2 前端有一个独立的「待确认资产」页面,把 `asset_candidates` 列出来,操作员看一眼流量识别出的清单,觉得"对,这确实是我们家的服务器",点一下"标记为资产",这条就从候选区转进正式台账,同时写一条确认记录。
这里有个我差点栽进去的坑:**确认结果得回传给 L3,让探针本地的状态跟 L2 对齐。** 不然 L2 标了资产,L3 还当它待确认,两边对不上,下次又推一遍,烦死了。
但 L3 大多躲在客户的 NAT、防火墙后面,L2 从外面推不进去。所以我没走"L2 主动推",而是让 **L3 自己来拉(PULL)**:L2 开一个 `GET /api/agents/{id}/asset-confirmations` 的端点,探针定时来问"我这边哪些被确认了",自己更新本地。这个弯绕了一下才想通,也算是一个人盯三套代码时,被网络拓扑教训出来的。
L2 还顺手做了两件事:一是资产分组,按业务系统、安全域、部门多对多归类,客户侧自己看得清;二是统一脆弱性台账,把 Web 扫描、数据库扫描、安全检查、资产扫描这四类来源的漏洞聚到一张表,带整改闭环(待整改→整改中→已整改→已验证),能派责任人、设期限、传验证证据。之前各扫描的漏洞只在页面上临时显一下,一刷新就没,落库之后才算真正接住了。
L1 只当"收集器":不确认、不回传、不看明细
到了一级平台(L1,云端 MSS 运营中心),我对它的定位特别克制:**只收聚合指标,不做确认。**
L2 按客户把资产指标聚成一份 `asset_summary` 往上送——总数多少、按类型怎么分布、漏洞危急高危中各几个、还有几条没确认。L1 收到之后,按 `customer_id` 覆盖写,每个客户只留最新一条。
然后 L1 拿这些聚合数据,画跨客户的资产态势大屏,给 MSS 那边的运营同学看全局。
这里有个老方案被我废了:早先有份文档写"资产维度预期 L3 直报 L1",现在我改成L2→L1 聚合上送,L3直连L1的情况下,只上报已确认资产。理由很简单,有L2,确认闭环在 L2,资产的主数据口径应该以 L2 为准。没有L2,那就是L3上自行确认。
说句实在话,L1 那块资产大屏现在还是半成品——前端卡片里漏洞修复率 88.7% 这种数字是硬编码占位的,后端接口还没接实时表。一人盯三套代码,这种"前端画好了后端没接上"的半成品到处都是,我也就不藏着了。
五、几个差点翻车的硬坑
这部分是我觉得最该讲的,因为都是一个人写三套代码、接口对不齐时最容易踩的。
**签名头写错,全量 403。** 资产上报的方案文档里,我手滑写成了 `X-Signature` 头。结果 L2 实际接收端是从 `X-Shepherd-Signature` 取签名,而且要求带 `sha256=` 前缀。照方案发过去,L2 拿到空签名,直接 403 拒收——资产上报一条都进不去。这种"文档和代码两张皮"的错,一个人写两头的时候特别容易出,发现时已经 debug 半天了。统一改成 `X-Shepherd-Signature` 才通。
**漏了 sent_at,L1 直接 400。** L2 和 L1 的接收端都强制验签,签名里必须带 `sent_at`。L3 现有的扫描、告警上报都带,资产事件同构就行。但 L2→L1 那份 `asset_summary` 上行,得先把 L2 侧的 `sent_at` 修好才能跑,不然 L1 一律 400 拒收。这个依赖关系联调前没对齐,就会卡一整晚。
**克隆虚拟机的 asset_id 撞车。** 资产 ID 我最早用 `uuid5(hostname + mac)` 生成,单机内稳定。可客户的虚拟机经常是克隆出来的,hostname 和 mac 一模一样,两个不同客户的资产就撞了 ID。解决办法是掺入 agent_id 或者部署时随机盐,L2 侧用 `(agent_id, asset_id)` 复合键隔离,跨客户碰撞的影响压到最低。
**customer_id 从 token 取,别信 payload。** 三套代码都得一致:L2 接收资产时,`customer_id` 从 agent 的 token 里解析,不读请求体里传的值。不然哪天 L3 传错一个字段,资产就归到别人客户名下了。这种跨级强一致,是我在文档里专门标了"联调前必查"的。
六、说到底
资产不是扫出来的,是确认出来的。
扫描只是手段,它解决的是"网里有什么";确认才是资产化的起点,它解决的是"哪些是真的归我管"。把这两步拆开,探针负责看全、看准,人负责拍板,系统负责确认后自动补齐扫描和台账,三级各管一段,链条才顺。
本系列记录牧安平台从 0 到 1 的开发过程,一个待业在家的网安老兵边做边想。
上一期:AI开发牧安 ·第10期 秀儿!把探针打包进一台飞牛 NAS
下一期:AI开发牧安 ·第12期 不懂研发架构的人搞开发,势必要踩的一些坑




