Skip to content

Latest commit

 

History

72 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ntop2ban

Watch the Top, Ban the Bad.

单机 Flow Analytics 平台。XDP/eBPF 采集 + ClickHouse 存储 + 灵活查询。

一个二进制,拷过去就跑。不需要 Elasticsearch、不需要装数据库


这是什么

以 ClickHouse 为核心,在单机部署场景下实现接近 ElastiFlow 核心 Flow Analytics 的能力:Top Talker / Conversation / ASN / Country / Port / Protocol / 时间序列 / 下钻,输入支持本机 XDP、远端 sFlow v5、远端 NetFlow v5。

下载

# x86_64
curl -L -o ntop2ban.tar.gz https://github.com/githubflyideas/ntop2ban/releases/latest/download/ntop2ban-linux-amd64.tar.gz
tar xzf ntop2ban.tar.gz && cd ntop2ban-linux-amd64
sudo ./ntop2ban -iface eth0 user=admin passwd=你的密码

四个平台各有一个这样的"解压即跑"包,把 URL 里的 linux-amd64 换掉即可: linux-amd64linux-arm64darwin-arm64darwin-amd64。包里三个文件:

ntop2ban-linux-amd64/
├── ntop2ban      # 主程序 ~10MB
├── clickhouse    # 官方静态二进制,由 ntop2ban 自动拉起托管
└── README.txt    # 这一页的浓缩版,离线也能看

压缩包 160~185MB(clickhouse 那个文件占了几乎全部;它是自解压的,首次 运行时会把自己展开到 770MB 左右,所以目标机上要留出约 1GB 空闲磁盘)。 clickhouse 必须在包里 —— ClickHouse 是唯一存储,没有兜底后端,这是 "不装数据库"的代价。

macOS 上是同样的流程,只多一步解除 Gatekeeper 隔离:

curl -L -o ntop2ban.tar.gz https://github.com/githubflyideas/ntop2ban/releases/latest/download/ntop2ban-darwin-arm64.tar.gz
tar xzf ntop2ban.tar.gz
xattr -dr com.apple.quarantine ntop2ban-darwin-arm64     # 这一步必须做
cd ntop2ban-darwin-arm64
sudo ./ntop2ban -iface en0 user=admin passwd=你的密码

xattr -dr 不能省。浏览器下载的压缩包会被打上 com.apple.quarantine, 解压出来的两个二进制都继承这个标记,包里的 clickhouse 既没签名也没公证, 直接运行会被系统拦下,而报错信息指不到隔离标记这个原因上。Intel 机器把 darwin-arm64 换成 darwin-amd64

macOS 上三种输入都能用,包括 -input local:本机抓包走 /dev/bpf,也就是 libpcap 在 Mac 上用的那套设施 —— BPF 本来就是 BSD 的东西,Linux 的 AF_PACKET + cBPF 是后来的仿制。纯 Go 实现,不需要 cgo。

两点与 Linux 不同,都会影响你看到的数字:

必须指定 -iface BIOCSETIF 是打开 BPF 设备的必要一步,没有 "监听全部网卡"这个语义。ifconfig 看名字,通常是 en0

抽样在用户态做,所以 macOS 上 -sampling 默认就是 1(全量),不用自己 写。 Linux 侧靠 cBPF 的 ExtRand 扩展在内核里就丢掉 (N-1)/N 的包;BSD 的 BPF 解释器没有 随机数扩展,判定只能等包拷到用户态之后。于是内核那侧的过滤、按 caplen 的 拷贝、缓冲区占用、read 系统调用,每个包都照付,N 是多少都一样 —— 抽样省 下来的只有头部解析与聚合那部分 CPU,而代价是短流会成片消失、且内核缓冲 溢出丢的包会被乘回 N 倍放大。Mac 与家用 NAS 的绝对包速本来就不高,这笔 交换不值当,所以默认值直接按平台分开:Linux 100、macOS 1。真要在 Mac 上 抽样,显式给 -sampling N 仍然生效,启动时会打一行日志说明它是在用户态 完成的。

还需要 root:/dev/bpf* 默认是 root:wheel 0600(Wireshark 装那个 ChmodBPF 启动项就是为这个)。不想用 sudo 就把设备属主改成当前用户。

macOS 上唯一真正缺的是 XDP —— 内核里没有可编程快路径这种东西,-datasource xdp-native 之类在 Mac 上不存在,只有 bpf-device 一级。

Linux 大包里的 clickhouse 是官方定版构建(packages.clickhouse.com/tgz/lts, 版本写在 Makefile 的 CH_VERSION),门槛是 glibc 2.4内核 3.2、CPU 要有 SSE4.2(x86-64-v2;arm64 要 ARMv8.2)。内核那条来自二进制的 .note.ABI-tag, 宿主机的 ld.so 读它、比自己旧就直接 FATAL: kernel too old —— CentOS 6 的 2.6.32 卡在这里,而它的 glibc 2.12 反而过得了 2.4。定版渠道没有 amd64compat,所以屏蔽了这些指令的 虚拟机与很老的物理机上跑不了内嵌实例 —— 那种机器请用 -clickhouse-addr 接一个 外部 ClickHouse,ntop2ban 自己是静态 Go 二进制,不受这些门槛影响。启动失败时进程 会直接把原因和该走哪条路打出来。

macOS 侧没有定版资产,继续用 builds.clickhouse.com/master 的自解压构建,它要 glibc 2.25 —— 在 Mac 上无所谓,写在这里只是解释两个平台为什么不一样。

sudo ./ntop2ban -iface eth0 -clickhouse-addr 127.0.0.1:9000 user=admin passwd=xxx

快速开始

sudo ./ntop2ban -iface eth0 user=admin passwd=你的密码
# 监听 :8090。默认只抓本机,不开任何 UDP 端口

需要 root(或 CAP_NET_ADMIN + CAP_NET_RAW)才能挂 XDP 与抓包。 发行包里 ntop2ban 与官方 clickhouse 静态二进制同目录,启动时自动 拉起并托管,不需要单独装数据库。已经有 ClickHouse 的话用 -clickhouse-addr host:9000 连过去。

输入源

三种输入,由 -input 选择。默认只有 local,不开任何 UDP 端口 —— 默认监听 UDP 意味着任何装上这程序的机器凭空多两个对外端口,而绝大多数 用户只想看本机流量。要收远端数据必须显式打开,那时你知道自己在开什么。

./ntop2ban -iface eth0                     # 只抓本机(默认)
./ntop2ban -input sflow                    # 只收 sFlow,不抓本机
./ntop2ban -input netflow -netflow-listen :9995   # 只收 NetFlow,换端口
./ntop2ban -input local,sflow -iface eth0  # 同时启用:本机 + 交换机镜像

最后那种组合是有实际场景的:一台机器既跑业务(本机流量)又收汇聚交换机 的 sFlow。三种输入产出同一套 Canonical Flow,进同一张表,查询时用 source_type 区分。

输入 适用 说明
local 单机、NAS、家用 抓本机网卡,不需要交换机配合。Linux 走 XDP/eBPF,macOS 走 /dev/bpf
sflow IDC、企业交换网络 远端设备 UDP 送采样包头,默认 6343
netflow 同上 NetFlow v5,默认 2055

sFlow 送的是采样到的原始包头(不是聚合好的 flow 记录),所以它复用 本机抓包那份包解析 —— 解出来的东西完全同构,这是"Input 可替换, Flow Model 不变"的落点。

收 sFlow:一次跑通

./ntop2ban -input sflow user=admin passwd=你的密码
# 日志里应该出现:sFlow v5 监听 :6343

端口已经是标准的 6343,交换机那边照设备手册填默认值就能对上,不用改 参数。要换端口才给 -sflow-listen :16343 —— 端口不写死是因为一台机器上 可能已经有别的 collector 占着 6343。

不需要 root。 6343 和 NetFlow 的 2055 都在 1024 以上,普通用户就能 bind。只收远端数据时整个进程都不碰网卡,所以别习惯性加 sudo

导出侧(交换机 / 路由器 / Open vSwitch)填三样:collector 地址是跑 ntop2ban 这台机器的 IP、端口 6343、采样率按链路带宽给(千兆给 1/1000 量级)。记得放开防火墙的 UDP 6343,这是最常见的"什么都没收到"的原因, 而 UDP 两边都不会报错。

确认数据真的进来了,按这个顺序看:

先看日志有没有 [sflow] 解码失败 —— 有,说明包收到了但格式对不上(常见是 设备发的是 sFlow v4 或 NetFlow,认错了协议)。一条也没有、界面也全是 0, 那就是包根本没到,回去查防火墙和导出侧配置,tcpdump -ni any udp port 6343 一眼就能分辨。

界面上则用 source_type 这个字段确认:它取 SFLOW / NETFLOW / LOCAL_XDP,三种输入进的是同一张表,按它过滤或分组就知道哪一路有数据。 同时开 -input local,sflow 时更要这么看,否则本机流量会盖住"sFlow 没收到" 这件事。

认证

照搬 pingping 的做法:用户名密码放启动参数,没有数据库、没有注册流程。

./ntop2ban user=alice,bob passwd=p1,p2

会话只在内存里,重启即失效——单机工具完全可以接受,换来每个请求零 I/O。 不带账号参数时生成随机密码而不是裸奔放行:这个界面能看全网流量明细、 能看到内网拓扑,代价太大;也不用固定默认密码,那在公网上等于没密码。

数据面:XDP 优先,自动降级

本机采集用一个 XDP 程序(bpf/sampler.c)做 1/N 抽样,命中的包经 ringbuf 送到用户态聚合。抽样判定在内核完成,不命中的包根本不会拷上来。

XDP 程序永远 XDP_PASS,只观测不拦截 —— ntop2ban 的职责是 Observe/Analyze,所以它不需要跟任何封禁程序争抢网卡挂载点。

出向要另挂一个钩子

XDP 只在接收路径上,发出去的包压根不经过它。所以出向另有一个程序,与 XDP 那个共用同一个 ringbuf、同一张抽样率 map,产出的观测只多一个方向 标记。内核给的两个出向钩子各有取舍,按这个顺序尝试:

钩子 内核 取舍
TCX(clsact egress) ≥ 6.6 按网卡挂,与 XDP 语义对称,转发的包也看得见
cgroup_skb/egress ≥ 4.10 几乎哪都能挂,但只看得见本机进程发出的包(转发的看不见),且需按 ifindex 过滤掉其它网卡

两个都挂不上时只警告、不退出:入向数据仍然完整,少一个方向也比整个 采集起不来强。但那行警告要当真 —— 那种状态下图上只有下载没有上传, 拿它判断带宽会得出反过来的结论。出路是把内核升到 6.6 以上,或者改用 -datasource af-packet(它本来就双向可见,代价是没有 XDP 那级性能)。 挂上不等于有数据:内核版本、钩子类型、网卡过滤这三件事出问题时 attach 都 不报错。所以不用去翻日志确认 —— 设置页有一块采集自检,直接写着走的 是哪一层、出向挂在哪个钩子上、两个方向各有没有数据,以及没有数据时的后果 和出路。af-packet 与 macOS 的 /dev/bpf 分不清方向,那里会说明两个方向 是合并统计的,不会误报成"上传没采到"。

TSO 会让包数少算四十倍,如果不管它。TC 钩子上看到的是一个还没切片的 大 skb(可以是 64KB),内核之后才拆成几十个网线包 —— 字节数基本是对的 (少的是多出来那几十份 IP+TCP 头,约 3%),但按"一个 skb 算一个包"来计, pps 就成了实际的 1/44。所以出向观测带上 gso_segs,聚合时按它累加。cgroup_skb 那条退路上不读这个字段:它的 可访问字段范围更窄,verifier 一旦拒掉会连带让整个 eBPF collection 加载 失败、把本来好用的入向 XDP 一起带走。同理,出向两个程序都是可降级加载的 (完整 → 去掉 cgroup → 再去掉 tc),宁可少一个方向,不能让入向陪葬。

挂不上 XDP 时按 native → generic → af-packet 逐级降级

层级 说明
xdp-native 驱动层处理,性能最佳。需要网卡驱动支持
xdp-generic 内核在 netif_receive_skb 处模拟。任何网卡都能挂,但已在 sk_buff 分配之后。veth、容器、部分云主机常只能走这级
af-packet 完全不用 XDP。内核太老、XDP 被占用或权限受限时的退路。抽样判定仍在内核侧完成(cBPF 的 ExtRand)
bpf-device macOS/BSD 上唯一的一级,/dev/bpf + cBPF。不与上面三级构成降级关系:XDP 在 macOS 上不存在,而 BPF 是 BSD 原生设施。抽样在用户态

各级产出完全相同的 Canonical Flow,用的是同一份包解析 (internal/flow)与同一个聚合器。三处各写一份解析迟早会在"长度算不算 以太网头""分片怎么处理"这类细节上分叉,而分叉的表现是同一份流量在 不同输入方式下显示出不同数字,没有任何报错。

降级只发生在启动时,一次决定、之后不变。运行时切换会让同一时间窗口内 混入两种口径的数据,曲线上出现无法解释的跳变。用 -datasource 可强制 指定某一级(排查用)。

富化

写入时富化,不在查询时 JOIN —— 亿级 flow 表与 GeoIP 表实时 JOIN 在单机上 不可行。代价是 GeoIP 库更新后历史数据保持当时的快照,这是想要的行为: 一个 IP 去年属于 A 公司今年属于 B,去年的流量不该被改写成 B 的。

在界面「设置」页一键同步,内置这些源,全部免费、无需注册:

类型 填充字段 许可
iptoasn.com ip2asn ASN ASN、国家(ISO)、组织 公共领域
DB-IP ASN Lite ASN ASN、组织(公司全称更规整) CC BY 4.0
DB-IP City Lite 城市 国家、省/州、城市、经纬度 CC BY 4.0

列表里只留能真正下下来的源。APNIC 的分配记录与纯真的文本导出都曾在列表 里,现在删掉了:ftp.apnic.net 在不少家宽出口上直接超时,而纯真那个文本 导出的仓库早已不再更新、URL 也时好时坏。摆一个点了必然失败的按钮比不摆 更糟 —— 用户会先怀疑自己的网络或这个程序。

也可以用 -ip2asn ./ip2asn-v4.tsv.gz 指定本地文件。同步过的库存在数据 目录里,重启后自动加载,不用每次重新点。

DB-IP City Lite 让城市维度成为默认可用的功能 —— 它是唯一免费且无需 注册的城市库,装上之后 Top City 与经纬度就有了,不再必须去 MaxMind 注册。 MaxMind GeoLite2 精度更高但需要 license key,所以只能手动下载后从界面上传, 不内置自动同步(那等于替用户接受了他没读过的许可协议)。

字段优先级是定死的,因为"我装了库为什么某一列还是空的"是最常见的疑问:

  • asn / org 来自 ASN 类源
  • country / region / city / 经纬度同时来自城市类源 —— 城市库带 ISO 码时它的 country 覆盖 ASN 库给的那个

城市库覆盖 country 是实测逼出来的:114.114.114.114 在 ip2asn 里归 US(按 BGP 路由归属,该前缀确实被一个美国 AS 宣告),而 db-ip 定位到山东济南 —— 保留 ASN 库的 country 会产出 country=US / city=济南 这种自相矛盾的行,而矛盾就在同一行里,用户第一眼 就会看到且无法解释。让 country/region/city 三者来自同一个源才自洽。

界面

七个视图:

视图 内容
实时 内存里刚收到的几百条记录、每个输入源的到达计数,回答「现在到底有没有包进来」;不查 ClickHouse
Dashboard KPI 卡片(总流量/包/流/活跃源 IP/目的端口)、流量趋势(堆叠面积)、Top Talkers / Destinations / 端口 / ASN、应用与协议构成(甜甜圈)
Hosts Top 源/目的主机,点 IP 下钻到该主机的对端、端口、应用、国家、ASN、协议
Conversations 源 ↔ 目的 的流量对,两端都可点击下钻
ASN / Country 源/目的国家、ASN、组织、城市(需 GeoLite2)
Geo Map 世界地图按国家着色(源/目的 × 流量/包/流),点国家下钻
Explorer 查询构造器:选字段、运算符、值,提交 AST;可查看生成的 SQL;查询条件可保存复用

所有数据都走 POST /api/v1/query 提交 Query AST,每个卡片、每次下钻 都是一次 AST 请求 —— 同一个引擎服务所有视图。

图表用 ECharts,资源 go:embed 进二进制,不引 CDN。内网机房拉不到 CDN 会直接白屏,而这种故障从二进制本身完全看不出原因,所以宁可让二进制 多 1MB。世界地图底图同样入库(Natural Earth 50m,feature 名就是 ISO alpha-2 码,与 src_country 精确对应,不做国名模糊匹配),由 /static/ 服务:入库的是预压缩资源,浏览器接受 gzip 就原样吐字节,ETag 命中走 304。

Explorer 是查询构造器:分组维度与统计指标都可以多选,时间粒度分桶,按任 一选出来的列排序;条件分「必须满足」与「排除」两块,后者编译成 NOT(...)。 切到明细模式则不聚合,直接列原始流记录的 25 个字段。可以点「查看 SQL」看 后端究竟生成了什么 —— 组合出复杂查询而结果不对时,没有这个入口只能靠日志 猜。

实时页只回答一个问题:现在有包进来吗。它读的是进程内存里刚收到的那几百 条记录,不查 ClickHouse —— 中间隔着攒批、INSERT 与 part 可见,几秒的空窗里 「还没落库」和「根本没数据」在 Explorer 上长得一样,写库失败了更是永远一样。 所以这一页有数而 Explorer 没数,本身就是结论:采集是好的,写库出了问题。 列表两秒增量刷新一次,只取比游标新的记录,不重画整张表。

加上 -dns-resolve 之后,表格里的 IP 后面会跟一个域名。域名是注解,不替换 IP —— IP 才是能拿去过滤、下钻、跟别的工具对照的标识,域名只帮人认出这是哪 台机器。反查在显示时按需做,不写进数据库、也不在采集路径上:进程内起一个纯 转发的解析器,答案缓存 300 秒(查不到的也缓存),浏览器侧再记一层,所以同一 个地址一页里只问一次、一个进程里 300 秒内只向上游问一次。默认读 /etc/resolv.conf,也可以用 -dns-upstream 指一个。

设置页可以填一份全局排除网段清单(默认「两端都在清单内」才排除),它 在服务端叠进每一条查询,所有视图与卡片共用同一个定义 —— 内网互访这类噪音 只需要写一次。

KPI 卡片把估算值与实测值并列展示,让人能判断这个数字是量出来的还是 算出来的。

数据模型

Canonical Flow 是整个系统最重要的接口:Input 可替换,Flow Model 不变。 将来加 NetFlow v9 / IPFIX 只需要新增一个 Normalizer,ClickHouse 表结构与 Query Engine 一行都不用改。

计数保留双份:packets/bytes 是按采样率还原的估算值, observed_packets/observed_bytes 是采样器真正看到的实测值。 只存估算值的话采样率事后发现配错就回不去了;只存实测值的话每次查询都 要乘一遍,而采样率是逐流可变的,那要求把采样率带进 GROUP BY,聚合基数 会暴涨。界面上两者分开展示。

长度统一取 IP 头声明的 total length,不是抓到的字节数——sFlow 只带包头 前 128/256 字节,用抓到的长度统计会让所有数字系统性缩水,而且完全静默。

存储

ClickHouse 是唯一存储,没有兜底后端。之前那套 FlowStorage 接口 + SQLite 兜底已删除:维护两个后端的代价没有换来对应价值,SQLite 版本永远 做不到分层聚合与亿级明细查询,而那正是这个产品的核心能力。

  • flows —— Raw Flow,MergeTree,按天分区,TTL 可配(-retention-days)
  • flows_1m —— 分钟级聚合,SummingMergeTree + 物化视图自动填充,供时间 序列与长期趋势(TTL 更长)
  • ip_metadata —— IP 维度权威源,ReplacingMergeTree

ORDER BY 目前是 (timestamp, src_ip, dst_ip, src_port, dst_port), 对应最高频的"最近 1h/24h + 某个 IP"。这是草案——最终必须通过真实 query benchmark 决定,而不是凭经验。

富化在写入时做,把 country/ASN/org 快照到 flow 行上,查询时不 JOIN (禁止让亿级 flow 实时 JOIN GeoIP 表)。代价是 GeoIP 库更新后历史数据 保持当时快照,这是想要的行为:历史应该反映当时的归属。

一台机器布 ClickHouse,别的节点写进来

这是支持的,而且不需要给每个节点单独的表或单独的库。所有节点写同一张 flows,靠 device_id 区分 —— 那个维度已经在预聚合表的 ORDER BY 里, 按它分组、过滤、画趋势都不用额外做什么。分表反而会坏事:跨节点的对比要 自己 UNION,物化视图和 TTL 要成倍维护,而 ClickHouse 本来就是为多个写入端 同时插同一张表设计的。

存储那台:

NTOP2BAN_CLICKHOUSE_PASSWORD='换成你的密码'   ./ntop2ban -clickhouse-listen 0.0.0.0 -node-id 1 -iface eth0 user=admin passwd=xxx

其余每个节点:

NTOP2BAN_CLICKHOUSE_PASSWORD='同一个密码'   ./ntop2ban -clickhouse-addr 10.0.0.10:9000 -node-id 2 -iface eth0 user=admin passwd=xxx

-node-id 必须各不相同 —— 本机采集的记录 device_id 原本恒为 0,不给 编号的话几个节点的流量在库里堆成一坨,而这件事不会报错。

权限上有一处要注意:store.Open 每次启动都会跑一遍 CREATE DATABASE / TABLE / MATERIALIZED VIEW IF NOT EXISTS(schema 是存储层的实现细节,不想 让部署者手工维护一份 DDL 并保持同步),所以节点用的账号需要建库建表的权限, 只给 INSERT 会在启动时失败。要收紧到只读只写就自己先把 schema 建好、 再单独开一个 INSERT 账号 —— 但那样每次升级都要自己补 DDL,单机部署不建议。

启动参数

./ntop2ban -h 会打印这份清单,下面按用途分组,顺带说清默认值的理由。

参数 默认 说明
-addr :8090 Web 监听地址。只想本机访问就写 127.0.0.1:8090
-input local 输入源:local(本机抓包)/ sflow / netflow,逗号分隔可同时开
-iface 本机抓包的网卡。XDP 与 macOS 的 /dev/bpf必须指定
-sampling Linux 100 / macOS 1 本机抓包抽样率 1/N;1 为全量
-datasource 空(自动降级) 强制采集层:xdp-native / xdp-generic / af-packet / bpf-device(macOS)
-sflow-listen :6343 sFlow v5 监听地址(标准端口)
-netflow-listen :2055 NetFlow v5 监听地址(标准端口)
-data-dir ./ntop2ban-data 数据目录。托管模式下 ClickHouse 的库文件也在这里
-clickhouse-addr 空(托管子进程) 外部 ClickHouse 的 host:9000;给了就不再拉起子进程
-clickhouse-bin 空(同目录 ./clickhouse) 托管用的 clickhouse 二进制路径
-clickhouse-listen 127.0.0.1 托管的 ClickHouse 自己监听哪里。要让别的节点写进来就填 0.0.0.0
-clickhouse-user default ClickHouse 账号。密码走环境变量 NTOP2BAN_CLICKHOUSE_PASSWORD
-node-id 0 本节点编号。多个节点写同一个 ClickHouse 时各给一个
-retention-days 90 明细数据保留天数,靠 ClickHouse 的 TTL 落地
-dns-resolve 显示时按需反查 IP 的域名,结果只用于展示,不入库
-dns-upstream 空(读 /etc/resolv.conf) 反查用的上游 DNS,hosthost:port
-dns-ttl 300s 反查结果的缓存时长,查不到的结果同样缓存这么久
-ip2asn ip2asn TSV(.tsv / .tsv.gz),提供 ASN / 国家 / 组织
-mmdb GeoLite2-City mmdb,额外提供城市与区域;也可在界面上传
-version 打印版本后退出
user= passwd= 无(生成随机密码) 位置参数,不带 -;逗号分隔多账号,两边个数要一致

几个不那么显然的:

-iface 什么时候是必需的。 XDP 要挂在一块具体网卡上,macOS 的 BIOCSETIF 也没有"所有网卡"这种语义,所以本机抓包必须给 -iface。 反过来,只收 sFlow / NetFlow 时它完全不用给 —— 那条路不碰网卡。

-sampling 为什么两个平台不一样。 Linux 上抽样判定在内核里由 cBPF 完成,不命中的包根本不会拷到用户态,100 几乎不花钱。macOS 的 BPF 没有 对应的随机数扩展,抽样只能在用户态做 —— 包已经拷上来了,省下的只有解析 与聚合,而统计误差按 1/√(观测包数) 变大。省得有限、损失确定,所以 macOS 默认全量。家用带宽下全量本来也不重。

-datasource 是排障用的,平时别给。 不给时按 native → generic → af-packet(macOS 上是 bpf-device)逐级试,失败原因会打在启动日志里。给了 就锁死在那一层,挂不上直接退出 —— 想确认"这台机器到底能不能上 XDP"时 才有用。虚拟机里的虚拟网卡通常只支持 generic。

-clickhouse-addr-clickhouse-bin 是二选一。 前者留空时才会去 拉起后者所指的二进制(默认找可执行文件同目录的 ./clickhouse,发行大包 里就是这么摆的)。已经有 ClickHouse 实例的话给 -clickhouse-addr,大包 里那个 clickhouse 直接删掉也行。

-retention-days 只影响明细表。 预聚合表(1 分钟 / 1 小时)保留更久, 所以删掉明细之后长期趋势图还在。改小它是回收磁盘最直接的手段。

富化库两个都可以不给。 不给就只有 IP 和端口维度,国家 / ASN / 城市 是空的。界面「设置」页里点一下同步就会自动下载并缓存到 -data-dir, 下次启动自动加载,不需要这两个参数;-ip2asn / -mmdb 是给离线机器 预置库文件用的。

API

端点 说明
POST /api/v1/query 提交 Query AST,返回 columns + rows + 执行统计
POST /api/v1/query/explain 返回将要执行的 SQL,不真正执行
GET /api/v1/query/fields 可用字段、运算符、指标(界面据此构造查询器)
GET /api/v1/overview 存储状态、输入源、富化库状态
GET /api/v1/queries 已保存的查询列表
POST /api/v1/queries/save 保存一条查询(存界面选择,不是 SQL/AST)
POST /api/v1/queries/delete 删除一条已保存的查询
POST /api/v1/enrich/mmdb 上传 GeoLite2-City,立即生效

Query AST 示例:

{
  "time_range": {"from": "2026-08-01T00:00:00Z", "to": "2026-08-01T01:00:00Z"},
  "filters": {"op": "AND", "conditions": [
    {"field": "src_country", "operator": "eq", "value": "JP"},
    {"field": "dst_port", "operator": "in", "value": [443, 8443]}
  ]},
  "group_by": ["dst_ip"],
  "metrics": ["bytes", "packets", "flows"],
  "sort": {"field": "bytes", "desc": true},
  "limit": 100
}

时间范围、limit、timeout 三样都是强制的:缺任何一个都能让一次误操作 变成一次故障 —— 没时间范围的聚合在单机上一次就能把 ClickHouse 打满。 字段与运算符都有白名单,而且运算符是逐字段限制的:src_ip 不给 like(在 IPv6 列上做字符串匹配能跑但结果反直觉),bytes 不给 cidr

IP 字段的 cidr / not_cidr 直接写网段,例如 10.252.145.0/24 —— 把自己的 内网从图里排除掉就是 src_ip not_cidr 10.252.145.0/24 加一条 dst_ip not_cidr 10.252.145.0/24。IPv4 存的是 IPv4-mapped IPv6,换算由服务端 做,不用自己写成 ::ffff:10.252.145.0/120;网段写坏了会直接报错,不会静默 返回空结果。

从源码构建

make build       # 构建 ./ntop2ban
make check       # vet + 全部测试
make release     # 交叉编译 {linux,darwin}/{amd64,arm64} 到 dist/(package 的输入)
make package     # 上面四个再各配一个 clickhouse 打成 tar.gz(要联网下 ~660MB)
                 # 产物就是全部发行资产:四个 tar.gz + SHA256SUMS
make verify-packages  # 用 file(1) 复核包里两个二进制的架构对得上

最终用户不需要 clang。 编译好的 eBPF 目标文件已提交进版本库。 只有改动 bpf/sampler.c 的维护者才需要:

make bpf         # 需要 clang + libbpf-dev,产物要一并提交
make bpf-verify  # 重新编译并与库里的 .o 比对(CI 跑这个)

bpf-verify 挡住的是"改了 C 忘了重编"——那样 .o.c 会静默漂移, 运行时行为与源码不符,极难排查。

CGO_ENABLED=0 是硬约束,所以能静态编译、拷过去就跑。

当前进度

  • Canonical Flow 模型 + 共用包解析(三种输入口径统一)
  • ClickHouse 存储层(flows / flows_1m / ip_metadata,托管子进程)
  • 本机采集:XDP 优先,三级降级
  • sFlow v5 / NetFlow v5 Collector 与 Normalizer
  • 写入时富化(ip2asn / DB-IP 一键在线同步,IANA 服务名分类)
  • Query AST 与查询引擎(字段白名单、强制时间范围与 limit)
  • Dashboard / Hosts / Conversations / ASN-Country / Geo Map / Explorer
  • 实时页(读内存,不查库)与显示时 DNS 反查(300 秒缓存)
  • 认证:启动参数 + 内存会话
  • Saved Query(查询条件保存复用)
  • Dashboard 自定义(卡片增删与布局)
  • 向 xdp-ban 推送可疑事件(POST /api/v1/security/events)
  • Benchmark 定稿 ORDER BY

已知限制

出向依赖较新的内核。 TCX 要 6.6,而 Debian 12 是 6.1、Ubuntu 22.04 是 5.15,这些机器上走 cgroup 那条退路,于是经过本机转发的流量在出方向上 看不见(只有本机进程发出的包能采到)。做路由/网关的机器要完整的出向 视图,目前只能用 -datasource af-packet

发行包必须用 Go 1.23.x 编译。 Go 1.23 产出的 Linux 二进制最低要内核 2.6.32,1.24 起抬到 3.2 —— 产物看不出区别,到了老机器上才 FATAL: kernel too oldmake release 会拦住 1.24 以上的工具链(确认不再支持老内核时加 ALLOW_NEW_GO=1)。内核低于 3.2 的机器上内嵌 ClickHouse 也用不了,只能 -clickhouse-addr 接外部实例。

ORDER BY 是草案。 当前 (timestamp, src_ip, dst_ip, src_port, dst_port) 对应最高频的"最近 1h/24h + 某个 IP"。设计文档明确要求最终 必须由真实 query benchmark 决定,而不是凭经验 —— 这件事还没做。

License

Apache-2.0

About

Watch the Top, Ban the Bad.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages