用 Go 编写的 Spip 网络传感器
Spip 是一个轻量级、低交互的网络蜜罐传感器。它监听任意的入站 TCP 流量(明文和 TLS),捕获扫描器和机器人发送的内容,并将每个连接记录为结构化的 JSON(符合 ECS 格式),便于导入 SIEM 或数据湖。
Spip 传感器为 HoneyLabs 平台提供数据,这是一个免费、可查询的威胁情报平台。要了解 Spip 在实际中采集的内容,可以浏览那里的实时按 IP 报告,或传感器网络生成的每周威胁报告。

前置条件
iptables 的 Linux 系统git clone https://github.com/honeylabshq/Spip-Go.git
cd Spip-Go
go build -o spip-agent ./cmd/spip-agent
sudo ./scripts/initial_setup.sh
该助手会写入 config.toml(它会提示输入一个在日志中使用的简短 name),可以生成自签名 TLS 密钥,可选地配置 Loom(URL、sensor_id、token 等),并可选择应用下方示例中使用的 PREROUTING iptables 重定向。
config.toml
最简 config.toml:name = "spip-agent"
ip = "127.0.0.1"
port = 8080
可选配置键:
cert_path / key_path — 两者都设置时启用 TLS;路径可相对于配置文件(安装脚本写入相对路径,以便配置从任何工作目录都能工作)log_file(本地)和/或 [loom](远程)。参见下面的日志输出。read_timeout_seconds / write_timeout_seconds — 连接超时rate_limit_per_second / rate_limit_burst — 连接速率限制community_id_seed — 可选的 16 位种子,用于 Community ID v1 流哈希(省略或设为 0 则使用默认值)如果这些运行时调优字段被省略或设为 0,Spip 将应用以下默认值:
read_timeout_seconds:30write_timeout_seconds:10rate_limit_per_second:20rate_limit_burst:50000sudo iptables -t nat -F
sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j RETURN
sudo iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-port 8080
./spip-agent -config config.toml
Spip 将 ECS 日志写入一个本地目标,并可可选地将相同日志发送到 Loom 服务器:
| 目标 | 配置 | 行为 |
|---|---|---|
| 本地 |
所以:本地默认输出到 stdout;用 log_file 覆盖为文件。可选地增加 Loom。两者使用相同的 ECS 格式。
log_file 注释掉/留空(stdout)或设置为路径。url、sensor_id、token 的 [loom] 段(参见下面的 Loom)。Spip 将每个连接输出为一个 JSON 对象。输出格式符合 ECS,仅包含 Spip 能提供的字段(无 ASN/地理信息)。生成的典型字段包括:
@timestamp — 事件的 RFC3339 时间戳event.id — 每个连接的会话标识符observer.hostname / host.name — 配置中的 agent namesource.ip、source.port 以及 destination.ip、destination.portnetwork.transport — 例如 tcphttp.request.body / url.path — 当有效负载明显为 HTTP 时user_agent.original — 当可用时Spip 生成的示例(符合 ECS 格式)记录:
{
"@timestamp": "2025-12-01T19:35:18.123Z",
"event": {
"id": "bd30cdc1-95b0-49aa-b8fe-e77230b6a04f",
"summary": "BitTorrent protocol",
"original_payload_hex": "426974546f7272656e742070726f746f636f6c",
"ingested_by": "spip"
},
"observer": {"hostname": "spip-agent"},
"host": {"name": "spip-agent"},
"source": {"ip": "146.70.1.1", "port": 35882},
"destination": {"ip": "146.190.1.1", "port": 6881},
"network": {"transport": "tcp"}
}
注意:agent 仅输出它能从连接有效负载和元数据中推断出的字段。下游系统可以对这些记录进行丰富(地理位置、ASN 等),如果希望的话。
Spip 可以为每个连接记录添加被动指纹识别字段(符合 ECS 格式,不影响有效负载捕获):
network.community_id) — 五元组(源/目的 IP 和端口、协议)的 v1 流哈希。当流量通过 iptables 重定向时,Spip 使用原始目的地址(在 REDIRECT 之前),因此哈希与其他工具(如 Zeek、Suricata)对同一流计算的结果一致。tls.client.server_name(SNI)、tls.client.supported_protocols(ALPN 列表)、tls.client.hash.ja4(JA4 指纹)。http.request.hash.ja4h(JA4H)。SSH-2.0- 开头且包含 KEXINIT 时:ssh.client.hash.hassh(Hassh)。所有这些字段都是附加的;现有行为(本地日志、Loom、有效负载十六进制、HTTP 解析)保持不变。
参考资料(用于验证和归因): Community ID:Corelight Community ID 规范。 JA4 / JA4H:FoxIO JA4。 Hassh:Salesforce HASSH。 TLS 指纹识别使用 github.com/psanford/tlsfingerprint(MIT 许可)。
日志输出的一部分:当 [loom] 中的 enabled = true 时,相同的 ECS 记录也会被批量 POST 到你的 Loom 摄取 URL。启用时需要:url、sensor_id、token。可选:batch_size(默认 50)、flush_interval(例如 "10s")、insecure_skip_verify(用于自签名 Loom 证书)。导出器异步运行,不会阻塞捕获循环;失败的 POST 会记录到 stderr,批次会被丢弃(故障开放)。
.
├── cmd/ # 主应用程序入口点
├── internal/ # 配置、日志、网络、TLS、指纹识别、导出器(例如 Loom)
├── pkg/ # Linux 套接字辅助函数(通过 syscall 实现 SO_ORIGINAL_DST)
├── test/ # 端到端测试辅助函数
└── scripts/ # 实用脚本(包括 `initial_setup.sh`)
运行单元测试:
go test ./...
端到端测试 需要操作 iptables 的权限。通过脚本运行(从仓库根目录):
sudo -E ./scripts/run_e2e_tests.sh
该脚本会设置环境和 iptables。或者使用容器辅助:./scripts/run_e2e_in_container.sh。端到端测试验证核心行为(有效负载捕获、源/目的、TLS 检测、Loom 批处理、指纹识别)。
Spip 对捕获的有效负载进行尽力而为的 HTTP 请求检测。当有效负载明显类似于 HTTP 请求(有效的请求行加基本头部或 ALPN)时,agent 会输出 http.*、url.path 和 user_agent.original 字段。否则,Spip 会将有效负载存储在 event.summary 中,并始终在 event.original_payload_hex 中保留原始有效负载的十六进制。
由于 Spip 会反映源 IP 并接受任意入站 TCP 流量,它旨在用作受控/监控环境中的蜜罐式传感器或边缘收集器,而非任意用户端点。
从仓库根目录运行交互式助手(应用 iptables 规则时需要 root 权限):
sudo ./scripts/initial_setup.sh
脚本会提示输入:一个简短的 name(写入 config.toml,在日志中作为 observer.hostname / host.name 使用)、监听 IP 和端口、可选的自签名 TLS 证书生成(路径相对于配置写入,以便从任何目录工作)、可选的 Loom 配置(URL、sensor_id、token、batch_size、flush_interval、TLS 验证)、日志文件路径,以及可选的 iptables PREROUTING 重定向。
log_file |
| 默认: 省略或留空 → stdout。设置为路径 → 该文件。二选一,始终启用。 |
| Loom | [loom] 且 enabled = true | 可选。相同的事件会批量 POST 到你的 Loom 摄取 URL,作为本地输出的补充。 |
event.summary — 非 HTTP 探测的原始有效负载event.original_payload_hex — 原始有效负载的十六进制(始终保留)network.community_id、tls.client.*、http.request.hash.ja4h、ssh.client.hash.hassh