快速开始

July 27, 2026 · View on GitHub

本文有两条路径,按你想验证什么来选:

需要什么大概多久适合
看判定逻辑只要 Node.js2 分钟理解策略怎么判、告警长什么样
跑完整系统Docker10 分钟接自己的流量评估、看控制面

第一条走参考引擎——零依赖的最小实现,用来验证算子语义。 第二条起的是真实形态:采集器 → Kafka → Flink → 告警 → 控制面。

前置条件

只需要 Node.js 18 或更高版本。没有其它依赖,不需要 Docker、数据库或消息队列。

node --version   # 应输出 v18 及以上

跑第一个场景

git clone https://github.com/threathunterX/nebula2.git
cd nebula2/packages/reference-engine
node run.js

你会看到类似这样的输出:

场景 credential-stuffing:策略 170 条、变量 253 个、事件 160 条,耗时 22ms
变量图节点 49 个(按策略引用的依赖闭包构建)
求值 12320 次,命中 114 条,去重抑制 1486 条

告警分布:
   42 条  IP请求登录前未访问必要资源
         主体: 198.51.100.77, 198.51.100.78, 198.51.100.35 等 42 个
   42 条  设备请求登录前未访问必要资源
    2 条  IP多次登录失败
         主体: 198.51.100.77, 198.51.100.78
   ...

这是一次撞库攻击的模拟:40 个正常用户零散登录,另有 2 个 IP 在短时间内高频尝试大量账号。

先看 IP多次登录失败 —— 它从 160 条事件中恰好挑出了这 2 个攻击 IP,正常用户一个没碰。

再看排在最前面那两条各命中 42 次的策略:它们把所有主体都打中了,包括 40 个正常用户。这不是 bug,而是内置模板的已知问题 —— 这两条策略含未配置的占位符,判定条件退化成恒真。它们在策略模板参考里被标为 🔧,提醒你配置后才能使用。

这正是"内置模板不能直接上生产"的直观例证。

跑起完整系统

需要 Docker(或 Colima 等等价实现)。

cd deploy/compose
./gen-env.sh          # 生成随机凭据到 .env,不含任何默认口令
docker compose up -d

会依次完成:建表 → 导入 170 条策略与 253 个变量 → 启动控制面 → 启动 Flink 集群。 种子导入是编排的一部分——库空着的时候控制面能登录但什么也管不了,而这不会报错。

管理员口令只在首次启动时打印一次:

docker compose logs console-api | grep -A4 已创建初始管理员账号

系统里没有默认口令,也不从配置文件读口令。确认资产已就位:

curl -u admin:<> localhost:8080/api/v2/stats
# {"events":17,"variables":253,"strategies":170,"tags":15,...}

提交引擎作业、灌事件、看告警的完整步骤见 Lite 部署说明。 Flink 界面在 http://localhost:8081。

这套 Lite 模式全部组件单节点、无高可用,面向评估与中小规模。Helm / Kubernetes 编排尚未开始。

看懂一条告警

node run.js --strategy "IP多次登录失败" --json

输出的告警里,最值得看的是 variable_values 字段:

{
  "key": "198.51.100.77",
  "check_type": "IP",
  "strategy_name": "IP多次登录失败",
  "scene_name": "ACCOUNT",
  "decision": "review",
  "expire": 1784945120144,
  "variable_values": {
    "page": { "value": "/api/login", "operator": "!regex", "threshold": "^\\s*$" },
    "result": { "value": "F", "operator": "==", "threshold": "F" },
    "count(c_ip) by c_ip in 600s": { "value": 6, "operator": ">", "threshold": "5" }
  }
}

它回答了运营人员最关心的问题:为什么判定这个 IP 有风险——因为本次登录失败,且该 IP 在过去 10 分钟内已经失败了 6 次,超过阈值 5。

这是 2.0 相对 1.x 的一处实质改进。1.x 也设计了这个字段,但代码里被写死为空字符串,运营看到告警却看不到依据。详见迁移指南

换一个场景

node run.js --scenario crawler

爬虫场景:一个 IP 在 5 分钟内请求 400 次商品页,背景是 40 个 IP 的正常浏览。

结果是 6 条不同的策略同时指向同一个 IP——大量动态请求、连续 GET、不带 referrer、不加载静态资源、相同 UA 请求单个页面、总访问量超阈值。六个独立角度的交叉印证,正常浏览的 IP 一个都没被命中。

这体现了风控策略的设计思路:单条策略容易被绕过,多角度覆盖才有对抗价值

这个场景里同样有占位符策略在误报,原因同上。

理解数据模型

系统的四层模型是:

事件 Event  →  变量 Variable  →  策略 Strategy  →  名单 Notice
业务行为       统计特征          判定规则          风险结论

刚才那条告警的完整链路:

  1. 攻击者发起登录请求 → 采集为 ACCOUNT_LOGIN 事件,result=F
  2. 引擎实时更新变量:该 IP 在窗口内的登录失败次数
  3. 策略「IP多次登录失败」的条件满足(本次失败 且 10 分钟内失败 > 5 次)
  4. 产出名单:该 IP 标记为 review,有效期 5 分钟

完整说明见风控数据模型

内置了什么

仓库的 seeds/ 目录是从 Nebula 1.x 继承并经过审计的风控资产:

资产数量说明
事件模型17HTTP_DYNAMIC 为根单继承,覆盖登录/注册/下单/支付/营销
统计变量253四层窗口:5 分钟实时、1 小时槽、当天、长期画像
策略模板170订单 70 / 账号 60 / 访客 40
风险标签15撞库、盗号、刷单、羊毛党、恶意扫描、爬虫等

这些模板不能直接用于生产,原因有三个,都写在策略模板参考里:

  • 处置动作全部是「转人工审核」,没有一条会自动阻断
  • 风险分未落地(169/170 条的 score 为 0)
  • 阈值来自 1.x 当年某个站点的经验值,必须按你自己的流量校准

另有 10 条策略含占位符,不配置则不会正常工作(其中 3 条会产生大面积误报),已在文档中用 🔧 标出。

跑测试

node --test 'test/*.test.js'

177 个测试,分两类:算子规格符合性(每条断言对应算子语义规格中的一条明文规定)和端到端行为

整个项目的测试分布:参考引擎 177、计算引擎 196、控制面 48、管理界面 9、采集器 6 个包。make test 全部跑一遍。

其中几个测试固化了「跑起来才发现」的事实,比如:

  • 未配置占位符的策略会打中几乎所有主体
  • 有 6 条策略因为把 contains 当正则用而永不命中

接下来

风控数据模型理解事件、变量、策略、名单四层
策略模板参考170 条内置策略逐条说明
算子语义规格每个算子的精确定义
系统架构生产形态的组件构成
接入指南如何接入自己的数据
Lite 部署起完整系统、提交作业

常见问题

参考引擎能用于生产吗?

不能。它是单进程、内存态、无持久化的,存在的意义是验证语义规格和作为 golden 测试基线。生产引擎基于 Flink,见 ADR-0002

为什么告警都标着 test: true?

内置策略模板以 test 状态分发——照常计算并产出告警,但不参与线上决策。这是刻意的:1.x 的出厂策略是 online 且生效时间窗全部已过期,照搬会导致导入后一条都不触发且没有任何提示。详见迁移指南

能用我自己的数据跑吗?

参考引擎只接受合成场景。接真实流量走完整系统那条路径:采集器支持 stdin / file / http 三种数据源,事件按敏感级别在采集端就地脱敏后再进 Kafka。见接入指南

另外请注意:本仓库的测试与示例数据严禁包含任何真实流量或个人信息,详见贡献指南