Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ntop2ban

Watch the Top, Ban the Bad.

小企业向的流量观测与访问控制。一个二进制,一个 SQLite 文件,scp 上去就能跑。


这是什么

ntop2ban 把三件事装进同一个程序:

  • 流量观测 —— XDP 采样(自动降级到 AF_PACKET),看清谁在打你的主机
  • 敲门授权(knock) —— 按预设序列敲门才放行 SSH,让扫描器看不到端口
  • 链路探测 —— 周期性 ICMP/TCP 探测,延迟与丢包的分布图(源自 pingping)

配上审批与审计:序列的变更需要走审批,成功授权全部留痕。

xdp-ban 的分工:xdp-ban 处理大流量镜像分析 (ClickHouse 分层聚合在那边),ntop2ban 走轻量路线。采样流量、敲门序列、 审批审计、探测结果都落同一个 .db 文件,拷走那个文件就是完整备份。

快速开始

./ntop2ban -api-key <你的密钥>
# 监听 :8090,数据落 ./ntop2ban-data/,采样保留 40 天

敲门(knock)

序列由用户在平台上设定,混合 TCP 与 ICMP,不用 UDP——很多客户端出口 环境发不出 UDP。整个序列必须在 1 分钟内完成。

一个典型序列 TCP 9001 → ICMP 56 → TCP 9003 → ICMP 90,敲门就是四条 系统自带命令,不需要任何自制客户端:

nc -z -w1 <host> 9001      # 第 1 步
ping -s 56 -c 1 <host>     # 第 2 步
nc -z -w1 <host> 9003      # 第 3 步
ping -s 90 -c 1 <host>     # 第 4 步
# 成功后 60 秒内可连接 SSH,只对你这个来源 IP 放行

界面直接给出可复制的命令,不用自己拼。

设计取舍值得说明:暗号是固定的,不做轮换。 曾经设计过按时间窗 用 HMAC 轮换 ICMP 包长,否决了——你得先去某处查当前值才能敲门,而那个 "某处"往往也在敲门保护之后,鸡生蛋。接受的代价是被抓包后可重放,但真正 要防的是全网扫描器,它永远猜不中这个序列;而能在链路上抓包的对手已经是 中间人,敲门本来也救不了,那时靠的是 SSH 自身的密钥认证。

只记成功,不记失败。 敲错的包就是互联网噪声,记下来只会淹没审计日志。

审批不是实时闸门:它管的是"序列定义"这份配置的变更(改哪些端口、 哪些 ICMP 长度),守护进程始终按当前生效的那版工作,不会卡在等审批上。

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

流量采样与敲门捕获共用一个 XDP 程序(bpf/sampler.c),一次包解析 两个输出:采样走 1/N 抽样(允许丢,只服务可视化),敲门走精确匹配 (一个包都不能漏)。两者用各自的 ringbuf —— 共用一个的话,高流量下 敲门事件会被采样事件挤掉,而那是最不能丢的东西。

XDP 程序永远 XDP_PASS,只观测不拦截。封禁下发到 nftables,那是 netfilter hook,与 XDP 不同层次,所以不存在网卡挂载点冲突。这个组合是 刻意的:采样是每个包都要过的路径,放 XDP 拿驱动层的性能;封禁是低频 规则增删,nftables 够用。

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

层级 说明
xdp-native 驱动层处理,性能最佳。需要网卡驱动支持
xdp-generic 内核在 netif_receive_skb 处模拟。任何网卡都能挂,但已在 sk_buff 分配之后。veth、容器、部分云主机的 virtio 配置常只能走这级
af-packet 完全不用 XDP。内核太老(<4.8)、XDP 被占用、或权限受限时的退路。抽样判定仍在内核侧完成(cBPF 的 ExtRand 扩展),用户态只收 1/N

流量展示是统一的:三级数据源产出完全相同的 model.Flow,用的是同一个 聚合器,曲线口径一致。唯一体现差别的地方是 Device 字段的模式标签, 以及界面顶部单独展示的"当前数据源"——性能差一个数量级,运维需要知道 自己在哪一级,否则会把"统计偏低"误判成"流量真的少"。

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

AF_PACKET 模式下敲门会自己开一个独立的精确捕获 socket,因为那一层的 采样过滤器带抽样,敲门包会被抽掉。

链路探测

周期性 ICMP/TCP 探测,记录每轮的 RTT 分布与丢包,画延迟/丢包图。 算法搬自 pingping, 但没有搬它的存储层——那边用 cgo 驱动,带进来会毁掉静态编译。 探测结果落 ntop2ban 已有的那一个库,只是多几张表。

探测随主程序一起启动,没有独立的命令。目标写在清单文件里,格式与 pingping 一致(从 pingping 迁过来可以直接拷原来的清单):

/etc/ntop2ban/ping.list     # ICMP:  host      [名字] [pace=fast|normal|slow] [interval=秒]
/etc/ntop2ban/tcp.list      # TCP:   host:port [名字] [pace=...]              [interval=秒]
echo '192.168.1.1  网关  pace=fast' >> /etc/ntop2ban/ping.list
echo '10.0.0.5:443  API网关  interval=30' >> /etc/ntop2ban/tcp.list
# 重启生效

用文件而不是数据库表:探测目标是运维手写的东西,echo host >> ping.list 比在界面上点几下或写 SQL 都快。首次启动会生成带注释的示例(全部注释掉, 不会擅自去探测某个示例主机)。清单为空时界面上的探测页会提示去哪个 文件加目标,而不是显示一张空图——空图让人以为程序坏了。

保留 pingping 的核心取舍:存分布而不是均值。一轮 20 个包,记下 min/p50/p90/p99/max。均值会把"一半包 5ms、一半包 500ms"和"所有包 250ms" 画成同一条线,而这两种链路的体感完全不同。丢包突发用 robust z-score (中位数 + MAD)判定,对异常值不敏感——用均值加标准差的话,历史上的 大丢包会把基线自己抬高,变成"以前抖过,现在抖就不算异常"。

启动参数

参数 默认 说明
-api-key —(必填) 外部采样器上报的鉴权密钥。留空会拒绝一切上报,因此启动时直接报错退出,而不是静默起来收不到数据
-addr :8090 HTTP 监听地址
-iface 观测网卡。XDP 模式必须指定;留空只能走 AF_PACKET
-sampling 100 抽样率 1/N;1 表示全量
-datasource 空(自动降级) 强制指定:xdp-native / xdp-generic / af-packet
-data-dir ./ntop2ban-data 数据目录
-days 40 数据保留天数(采样与探测共用)
-probe-dir /etc/ntop2ban 探测清单目录(ping.list / tcp.list)
-no-knock 不启动敲门

从源码构建

make build     # 构建 ./ntop2ban
make test      # 全部测试
make check     # vet + test
make release   # 交叉编译 linux/{amd64,arm64} 到 dist/

CGO_ENABLED=0 是硬约束:SQLite 用 modernc.org/sqlite(纯 Go), 所以能静态编译、拷过去就跑。

路线图

  • 采样存储层(SQLite)与接收端点
  • 敲门序列状态机与持久化(序列版本 + 成功授权记录)
  • 敲门捕获与 nftables 放行
  • 链路探测(ICMP/TCP,分布存储,突发判定)
  • 内化数据面:XDP 采样 + 敲门精确匹配,三级降级,展示统一
  • 用户与审计(admin / viewer 两级,admin 超级权限)
  • Web 界面(流量排行、探测分布图、敲门序列配置与审批、审计日志)
  • 写入时富化(GeoIP / ASN / IANA 服务名)
  • 从流量界面点击 IP 直接发起封禁

License

Apache-2.0

About

Watch the Top, Ban the Bad.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages