ecan0/serpent-wrt

GitHub: ecan0/serpent-wrt

一个专为 OpenWRT 路由器设计的轻量级威胁情报检测与 nftables 阻断守护进程,通过读取 conntrack 表实现低资源占用的网络异常行为检测。

Stars: 0 | Forks: 0

# serpent-wrt [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/280c68dfa6170703.svg)](https://github.com/ecan0/serpent-wrt/actions/workflows/ci.yml) [![Go 1.26](https://img.shields.io/badge/go-1.26-blue.svg)](https://golang.org/dl/) [![许可证: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) 一个轻量级的威胁情报与执行守护进程,专为 OpenWRT 路由器设计。 serpent-wrt 使用 Linux conntrack 元数据监控网络连接,通过行为启发式检测可疑活动,并使用 nftables 执行拦截——无需数据包捕获,无需数据库,内存占用极低。专为内存低至 64 MB 的受限设备设计。 ## 为什么选择 conntrack 而不是数据包捕获? tcpdump 和 libpcap 会将每个数据包复制到用户空间。在配备 64 MB RAM 和 400 MHz MIPS CPU 的路由器上,这会产生持续的每数据包 CPU 开销、环形缓冲区带来的内存压力,以及任何 PCAP 存储带来的闪存磨损——而威胁信号并不需要有效载荷检查。 Linux 内核已经在 `nf_conntrack` 中维护了一个紧凑的每流状态表。无论流量如何,读取 `/proc/net/nf_conntrack` 每个轮询周期只需一次文件读取。 | | 数据包捕获 | conntrack | |---|---|---| | CPU 成本 | 每数据包 | 每轮询周期 | | 内存 | 环形缓冲区 + 重组装 | 内核平面表 | | 数据 | 每个字节 | 每流元数据 | | 磁盘写入 | 可选的 PCAP 文件 | 无 | | 执行 | 独立栈 | 复用现有 nftables | ## 架构 ``` /proc/net/nf_conntrack │ ▼ collector ── poll interval ──▶ FlowRecord{proto, src, dst, ports, state} │ ▼ direction classifier (skip: unroutable, broadcast, self) │ ├── LAN → WAN ──────────────────────────────────┐ │ ┌──────────────────────────────────────────┐ │ │ │ feed_match (threat feed hit) │ │ │ │ fanout (many distinct destinations) │ │ │ │ port_scan (many distinct ports) │ │ │ │ beacon (periodic C2 cadence) │ │ │ └──────────────────────────────────────────┘ │ │ │ └── WAN → LAN ──────────────────────────────────┘ ┌──────────────────────────────────────────┐ │ ext_scan (external recon: many ports) │ │ brute_force (spray: same port, many hosts│ └──────────────────────────────────────────┘ │ Detection event ▼ dedup filter ──▶ suppress repeat alerts (sliding window) │ ▼ event logger ──▶ NDJSON to stdout │ (+ optional remote syslog) ▼ nftables enforcer ──▶ inet blocked_ips set ``` ## 功能 - 基于 conntrack 的流量采集——无需数据包捕获 - 本地 IP/CIDR 威胁源,支持通过 `SIGHUP` 热重载 - 六个行为检测器,涵盖出站和入站方向 - 方向感知路由:LAN→WAN 检测器(fanout、port_scan、beacon、feed_match)独立于 WAN→LAN 检测器(ext_scan、brute_force)运行 - 广播、回环、链路本地和路由器自身流量在任何检测器处理前自动过滤 - 去重抑制在可配置时间窗口内压缩同一来源的重复告警 - 通过命名集合的 nftables 执行,由内核管理超时 - 结构化 NDJSON 日志输出到 stdout - 可选的远程 syslog 转发(UDP/TCP)用于 SIEM 集成——失败时自动重连 - 可选的仅本地主机 HTTP API - 单一静态二进制文件——无数据库、无 systemd 依赖,兼容 procd ## 构建 ``` # 本地二进制文件 make build # → bin/serpent-wrt # 为 OpenWRT 目标交叉编译 make cross # → bin/serpent-wrt-linux-{mipsle,mips,armv7,arm64,amd64} # x86/generic — 32位 OpenWRT 虚拟机 (uname -m = i686) GOOS=linux GOARCH=386 go build -o bin/serpent-wrt-linux-386 ./cmd/serpent-wrt # 运行测试 make test ``` **支持的目标平台:** `linux/mipsle`、`linux/mips`、`linux/arm`(v7)、`linux/arm64`、`linux/amd64`、`linux/386` ## 部署 ### 首次设置 ``` # 复制初始化脚本、配置和威胁情报到路由器 make deploy-setup DEPLOY_HOST=root@ # 构建并推送二进制文件 (x86/generic) make deploy-x86 DEPLOY_HOST=root@ ``` ### 手动部署 许多 OpenWRT 构建版本省略了 `sftp-server`,因此 `scp` 和 `rsync` 会失败。改用 stdin 传输文件: ``` # 覆盖二进制文件前先停止服务(避免"Text file busy") ssh root@router '/etc/init.d/serpent-wrt stop' # 传输二进制文件 ssh root@router 'cat > /usr/sbin/serpent-wrt && chmod +x /usr/sbin/serpent-wrt' \ < bin/serpent-wrt-linux-386 # 传输配置和情报 ssh root@router 'cat > /etc/serpent-wrt/serpent-wrt.yaml' \ < configs/serpent-wrt.example.yaml ssh root@router 'cat > /etc/serpent-wrt/threat-feed.txt' \ < testdata/threat-feed.txt # 传输初始化脚本、启用并启动 ssh root@router 'cat > /etc/init.d/serpent-wrt && chmod +x /etc/init.d/serpent-wrt' \ < contrib/init.d/serpent-wrt ssh root@router "/etc/init.d/serpent-wrt enable && /etc/init.d/serpent-wrt start" ``` ### 热重载威胁源 ``` # 无需重启重新加载 — 通过 procd reload 或直接触发: kill -HUP $(pidof serpent-wrt) ``` ## 配置 完整注解示例请参阅 [`configs/serpent-wrt.example.yaml`](configs/serpent-wrt.example.yaml)。 ``` poll_interval: 5s threat_feed_path: /etc/serpent-wrt/threat-feed.txt enforcement_enabled: false # set true once nftables is verified working block_duration: 1h # LAN CIDR — 用于方向分类(LAN→WAN 与 WAN→LAN) lan_cidrs: - 192.168.1.0/24 # 路由器自身的 IP — 源自这些的流量在检测器运行前被过滤 # 添加每个接口 IP(LAN、WAN,回环自动) self_ips: - 192.168.1.1 nft_table: serpent_wrt nft_set: blocked_ips log_level: info api_enabled: true api_bind: 127.0.0.1:8080 # 可选:将 JSON 事件转发到远程 syslog 目标(如 Wazuh on port 514) # syslog_target: "10.0.0.10:514" # syslog_proto: "udp" # or "tcp" detectors: fanout: distinct_dst_threshold: 50 window: 60s scan: distinct_port_threshold: 30 window: 60s beacon: min_hits: 5 tolerance: 3s window: 5m ext_scan: distinct_port_threshold: 15 window: 60s brute_force: threshold: 5 window: 60s ``` ## 威胁源格式 纯文本,每行一个 IPv4 地址或 CIDR。以 `#` 开头的行和空行将被忽略。 ``` # 示例 1.2.3.4 185.220.101.0/24 ``` ## 检测器 所有检测器仅基于连接元数据运行——不检查有效载荷。 **出站(LAN → WAN)** | 检测器 | 触发条件 | 关键配置 | |---|---|---| | `feed_match` | 源或目标 IP/CIDR 匹配威胁源 | `threat_feed_path` | | `fanout` | 内部主机联系过多不同的外部目标 | `distinct_dst_threshold`、`window` | | `port_scan` | 内部主机在单一目标上探测过多不同端口 | `distinct_port_threshold`、`window` | | `beacon` | 内部主机以固定间隔联系同一目标 | `min_hits`、`tolerance`、`window` | **入站(WAN → LAN)** | 检测器 | 触发条件 | 关键配置 | |---|---|---| | `ext_scan` | 外部 IP 在单一内部主机上探测过多不同端口 | `distinct_port_threshold`、`window` | | `brute_force` | 外部 IP 在多个内部主机上命中同一服务端口 | `threshold`、`window` | ESTABLISHED TCP 流被排除在 beacon 检测器之外,以避免持久连接产生误报。`feed_match` 检测器同时检查源和目标,以捕获来自已知恶意 IP 的入站连接。 ## API 当 `api_enabled: true` 时可用。仅绑定到本地主机。 | 端点 | 方法 | 描述 | |---|---|---| | `/healthz` | GET | `{"status":"ok"}` | | `/stats` | GET | 运行时计数器(已见流量、按类型检测、应用拦截) | | `/reload` | POST | 从磁盘热重载威胁源 | | `/detections/recent` | GET | 最近 100 条检测记录 | ## 日志 NDJSON 输出到 stdout,每行一个事件: ``` {"time":"2025-01-01T00:00:00Z","level":"warn","type":"detection","detector":"feed_match","src_ip":"192.168.1.5","dst_ip":"1.2.3.4","dst_port":443,"message":"connection to threat feed entry 1.2.3.4"} {"time":"2025-01-01T00:00:00Z","level":"warn","type":"enforcement","src_ip":"192.168.1.5","message":"blocked 192.168.1.5 triggered by feed_match"} ``` 配置 `syslog_target` 后,每个事件还会作为 JSON 字符串转发到 syslog MSG 字段。兼容 Wazuh、Graylog 和任何 RFC 3164 syslog 接收器。发送方在写入失败时自动重连,因此 SIEM 的短暂重启不会永久破坏远程转发。 ## 局限性 - **仅支持 IPv4** — IPv6 conntrack 条目被跳过 - **轮询而非事件驱动** — conntrack 按固定间隔读取;第 5 阶段将用 netlink 流式传输替换 - **无 DNS 关联** — 不解析或跟踪域名 - **不检查有效载荷** — 如上所述,这是设计意图 - **nft 子进程** — 执行调用 `nft` 子进程;在此规模下可接受 - **无持久状态** — 重启后检测历史和拦截状态丢失 - **仅本地威胁源** — MVP 中无远程源同步 ## 路线图 | 阶段 | 状态 | 范围 | |---|---|---| | 1 | 已完成 | 配置、流量模型、事件、源、conntrack 采集器 | | 2 | 已完成 | 源匹配、fanout、port_scan、有界滑动窗口状态存储 | | 3 | 已完成 | nftables 执行器、运行时管道、统计、API | | 4 | 已完成 | 信标检测器、procd 初始化脚本、测试、远程 syslog、自愈 UDP 写入器 | | 5 | 已完成 | 入站 WAN 检测(ext_scan、brute_force)、方向分类器、去重抑制、广播/自身过滤 | | 6 | 计划中 | Netlink conntrack 事件、dnsmasq 集成、IPv6、目标支持时的 eBPF/XDP、LuCI 插件 | ## 许可证 [MIT](LICENSE)
标签:CIDR扫描, conntrack, EVTX分析, Go语言, IoT安全, LangChain, Linux路由器, Netfilter, nftables, OpenWRT, 启发式检测, 威胁情报, 开发者工具, 恶意行为检测, 程序破解, 网关安全, 网络安全, 轻量级, 防火墙, 隐私保护