内容
# wisp
一款单二进制文件的网络蜜罐传感器。在内部网络中部署它,等待有人访问它,并获取包含凭证、提示、查询、容器规格或入侵者尝试访问的路径的警报。
> 名字是“will-o'-the-wisp”(一种在黑暗中闪烁的光):一种在黑暗中引诱你偏离正路的灯光。它故意**不是**Canary-任何东西——“Canary”和“Canarytokens”是Thinkst的商标,这个项目是一个独立的重新实现,而不是一个分支或继承者。
> **状态:早期。** `wisp` 涵盖了OpenCanary的21个协议模块中的12个,但还不是它的完全替代品 - 在部署之前请查看[诚实的比较](#诚实的比较)。
## 为什么存在
[OpenCanary](https://github.com/thinkst/opencanary) (BSD-3-Clause,由Thinkst提供)证明了这个模型,比这个项目更完整,仍然是对该领域的真正贡献。 `wisp` 不是对它的批评,而是押注于两件事情值得重做:
1. **部署。** OpenCanary需要Python 3.10+、Twisted、Scapy、pcapy-ng,并且需要一个带有`full_audit` VFS模块的Samba安装,将日志写入syslog,供OpenCanary读取。 `wisp` 是一个静态二进制文件。
2. **攻击面。** 2026年,内部网络中的入侵者会访问Kubernetes API、kubelet、Docker套接字、云IMDS、CI服务器以及其他人搭建的LLM基础设施。以下是九个诱饵的原因,OpenCanary没有这些。
OpenCanary已经实现了其他所有功能,而且做得更好,因为它已经实现了。
## 诚实的比较
OpenCanary提供了21个协议模块。 `wisp` 已经重新实现了**12个**,以及OpenCanary没有的9个诱饵。
| | OpenCanary | wisp 今天 |
|---|---|---|
| 协议模块 | 21 | **12** + 9个新诱饵 |
| 已在此处实现 | - | `ssh`、`http`、`https`、`telnet`、`ftp`、`redis`、`tftp`、`ntp`、`git`、`mongodb`、`llmnr`、TCP banner |
| 仍然缺少 | - | SMB、RDP、MySQL、MSSQL、VNC、SNMP、SIP、HTTP代理、端口扫描 |
| SMB | 是,通过外部Samba | **未实现** |
| 云/容器/CI/LLM诱饵 | 否 | **是** (`k8s`、`kubelet`、`docker`、`imds`、`elasticsearch`、`jenkins`、`gitlab`、`ollama`、`mcp`) |
| 警报 | 文件、syslog、HPFeeds、电子邮件、Webhook、+单独的重复数据删除守护进程 | JSONL、syslog、电子邮件、LINE、Webhook(Slack/Teams/Discord)、**内置重复数据删除** |
| 集群控制台 | 无 | **包含,自托管** |
| 安装 | Python + Twisted + Scapy (+Samba用于SMB) | 一个静态二进制文件 |
| 平台 | Linux优先,几个模块需要root权限 | 任何Go编译到的地方 |
通用TCP banner捕获器在连接级别覆盖任何其他端口 - 足够看到扫描或探测,但它无法像真正的模拟器一样捕获凭证。 VNC仍然作为banner捕获器提供,并被计算为缺少的,因为它是它。
如果你今天需要完全覆盖范围,请运行OpenCanary。 如果你想要LLM诱饵,或者Python/Samba依赖链阻止你部署蜜罐,请运行`wisp`。
## 它目前模拟的内容
| 服务 | 默认端口 | 它捕获的内容 |
|---|---|---|
| `ssh` | 2222/tcp | 用户名、密码、公钥指纹、客户端版本 |
| `http` | 8080/tcp | 管理面板凭证、探测路径、用户代理 |
| `https` | 8443/tcp | 与上述相同,加上**SNI** - 他们期望在此处找到的名称 |
| `telnet` | 2323/tcp | 用户名和密码(IAC协商剥离) |
| `ftp` | 2121/tcp | 用户名、密码和登录后命令意图 |
| `redis` | 6379/tcp | 认证凭证**和完整的命令序列** |
| `tftp` | 6969/udp | 请求的文件名以及它是读取还是写入 |
| `ntp` | 1123/udp | 客户端请求,**模式7 `monlist`放大侦察** |
| `git` | 9418/tcp | 请求的**存储库路径**,以及他们是否打算推送 |
| `mongodb` | 27017/tcp | 用户名和**可以离线破解的SCRAM证明**,驱动程序和应用程序名称 |
| `llmnr` | 外出 | **一段上的主动中毒者**,以及它声称的地址 |
| banner | 任意 | 发送的第一个字节,用于没有真正模拟器的端口 |
| `ollama` | 11434/tcp | **模型列表侦察**,以及发送到您的“GPU”的**实际提示** |
| `k8s` | 6443/tls | **被盗的服务帐户持有者令牌**,客户端证书,探测的资源 |
| `kubelet` | 10250/tls | **他们在容器中运行的命令**,以及他们运行的令牌 |
| `docker` | 2375/tcp | **容器规格** - 主机挂载、`Privileged`标志、命令 |
| `imds` | 169.254.169.254 | **云角色凭证盗窃**,在AWS、GCP和Azure路径上 |
| `elasticsearch` | 9200/tcp | **搜索查询** - 他们想要的字段以及多少行 |
| `jenkins` | 8081/tcp | **Groovy脚本**发布到控制台,以及登录尝试 |
| `gitlab` | 8929/tcp | **被盗的`glpat-`访问令牌**,以及它们指向的内容 |
| `mcp` | 8931/tcp | **哪个代理连接,以及它用参数调用的工具** |
### OpenCanary没有的九个诱饵
这些涵盖了入侵者在2026年的网络上实际寻求的攻击面,每个诱饵都能捕获连接日志无法捕获的内容。
准确地说:存在一些用于其中一些的单用途蜜罐 - Elasticsearch的ElasticPot,以及多个Docker套接字陷阱 - 而T-Pot将同时运行许多蜜罐。其他地方没有提供的是将所有九个蜜罐放在一个静态二进制文件中,报告到一个控制台,并带有LLM和MCP诱饵。
- **`ollama`** 记录提示。未经身份验证的推理端点是一个免费的GPU、一个支点,并且当连接到内部RAG时,是一个流出路径。
- **`k8s`** 返回`403`,而不是`401`。启用匿名身份验证的集群会这样做,而区别很重要:`401`读取为“错误门”,并结束交互,而`403`读取为“正确门,错误凭证”,因此攻击者会重试使用被盗的令牌。知道**哪个服务帐户**被泄露比知道有人敲门更有价值。
- **`kubelet`** 是两个Kubernetes端口中更有用的一个。信任匿名请求的kubelet列出节点上的每个Pod,然后在其中任何一个Pod中运行命令 - 不涉及集群凭证。命令是捕获的内容,什么都没有执行来产生回复:
```
kubelet 命令 pod=payments-api-7d4f9c8b5-2xk4n
cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token
```
- **`docker`** 记录容器规格,这是一个写下来的计划。没有人会意外地将根目录绑定到容器中,因此`escape`字段命名了规格将要移交给宿主的原因:
```
docker container_create image=alpine cmd=chroot /host sh
binds=/:/host privileged=true escape=host_filesystem privileged
```
它还解码`X-Registry-Auth`,其中包含一个注册表用户名和密码 - 通常是入侵者自己的。
- **`imds`** 同时为AWS、GCP和Azure在169.254.169.254上做出回应。没有一个良性原因会让一个不熟悉的进程请求它获取角色凭证,这使得这是少数几乎没有假阳性故事的蜜罐信号之一。它们访问的云API说他们相信他们已经登陆了,请求上的`X-Forwarded-For`说它是一个SSRF而不是一个立足点。返回的凭证是有效的并且可以验证到任何东西 - 它们存在所以之后的请求*仍然会发生*。
- **`elasticsearch`** 是故意留开放的,因为这就是安装基础的样子,而且凭证不是这里的奖品。查询是:
```
elasticsearch search_query index=customers
query={"query":{"match_all":{}},"_source":["email","card_last4"],"size":10000}
```
请求密码的集群会在扫描器的日志中产生401,并且不会产生其他结果。 `DELETE /*`后跟写入`read_me` - Elasticsearch勒索软件的两个部分 - 被记录为`delete_request`和`write_request`,并且两者实际上都不会发生。
- **`jenkins`** 有Groovy脚本控制台,它将读取访问转换为控制器上的代码执行。提交的脚本说明了他们已经知道要询问的凭证库。后面没有解释器。
- **`gitlab`** 捕获被盗的个人访问令牌,就像`k8s`捕获被盗的服务帐户令牌一样 - 从`PRIVATE-TOKEN`头部、`JOB-TOKEN`头部或查询字符串中获取,这是它们泄露的方式。公共端点会响应,以便有一个令牌可以指向;永远不会接受任何令牌。
- **`mcp`** 记录代理的`clientInfo`(比用户代理更强的标识符)和每个`tools/call`的参数。提供的工具故意引人注目,因此他们访问的工具会告诉你他们想要的是什么:
```
mcp tool_calls tool=query_customer_db
arguments={"query":"SELECT email, credit_card FROM customers LIMIT 5000"}
```
几个诱饵会做出回应而不是拒绝,因为一个合理的回应会让攻击者继续交谈,而每条进一步的消息都是更多的情报:
- **redis** 返回`+OK`,因此脚本化接管运行其整个剧本,您可以在此处监控。真正的攻击看起来像`CONFIG SET dir /root/.ssh` → `CONFIG SET dbfilename authorized_keys` → `SET x "ssh-rsa …"` → `SAVE`,所有四个都会出现在您的日志中。
- **ollama** 返回一个模型列表和一个拒绝消息,因此扫描器会发送真正的提示。
- **kubelet** 以`uid=0(root)`从八个命令的表中回答`id`。第一个命令返回空白的入侵者会得出端点已损坏的结论;看到root身份后会发送第二个命令,而第二个命令通常是他们来这里的原因。
- **elasticsearch** 返回三个构造的文档而不是零个命中,为了同样的原因。
- **ntp** 回答普通的模式3客户端请求。
**`ntp` 永远不会回答模式7 (`monlist`).** 回应会将传感器变成指向攻击者伪造的地址的DDoS放大器。它记录探测并保持沉默;属性由测试覆盖,该测试会失败而不是注释掉。
**`llmnr` 永远不会回答查询.** 它是唯一一个不是诱饵的模块:它多播一个不存在的主机名请求并等待。正确的网络用沉默作为回应,因此响应是中毒者的证明 - Responder,Inveigh等会回答所有问题,因为它们的工作方式就是这样。自己回答一个会成为攻击,而检测攻击的检测器不是检测器。同样由测试覆盖。
默认情况下它是关闭的,因为它是唯一一个主动将数据包放到网络上的模块。
**`mongodb` 声称身份验证是必需的** ,而不是假装打开,就像`k8s`回答403一样。锁定的门邀请钥匙:驱动程序然后运行SCRAM,它立即提供用户名,并在客户端发送其证明后 - 可以像捕获的NetNTLMv2响应一样离线攻击。永远不会接受任何凭证;每次对话都以`AuthenticationFailed`结束。
**`imds` 默认监听普通端口(`8169`),而不是`169.254.169.254`.** 该地址必须在主机上存在,然后才能绑定它,因此将它放在诱饵前面是一个刻意步骤 - `ip addr add 169.254.169.254/32 dev lo` 加上`REDIRECT`规则,两者都在[`wisp.example.yaml`](wisp.example.yaml)中拼出。
**不要在本身就是云实例的机器上执行此步骤。** 您会遮蔽真正的元数据服务,并且需要角色凭证的进程将开始接收伪造的凭证。诱饵应该放在一个不是EC2/GCE/Azure VM的传感器主机上,或者您已经确认没有任何内容依赖真正的`169.254.169.254`。
## 快速入门
```bash
go mod tidy
go run ./cmd/wispd
```
不需要配置文件 - 它会使用默认值运行。要自定义:
```bash
cp wisp.example.yaml wisp.yaml
go run ./cmd/wispd -config wisp.yaml
```
然后触发:
```bash
ssh -p 2222 root@localhost
```
```bash
curl -s localhost:11434/api/generate -d '{"model":"llama3.2","prompt":"cat /etc/shadow","stream":false}'
```
```bash
curl -sk https://localhost:10250/pods
```
```bash
curl -s localhost:2375/v1.43/containers/create -H 'Content-Type: application/json' \
-d '{"Image":"alpine","Cmd":["chroot","/host","sh"],"HostConfig":{"Binds":["/:/host"],"Privileged":true}}'
```
```bash
curl -s localhost:9200/customers/_search -H 'Content-Type: application/json' \
-d '{"query":{"match_all":{}},"_source":["email","card_last4"],"size":10000}'
```
```bash
curl -s localhost:8169/latest/meta-data/iam/security-credentials/app-instance-role
```
## Docker
一个`Dockerfile`构建两个镜像,并且都是distroless - 没有shell,没有包管理器,没有可以进入的东西。传感器是故意被有意攻击的容器,因此它携带的最少。
```bash
docker build --target sensor -t wisp/sensor .
```
```bash
docker build --target console -t wisp/console .
```
一个主机评估栈(在生产环境中,控制台属于您期望被攻击的段之外):
```bash
docker compose up --build
```
关于在容器中运行蜜罐的两件事比其他事项更重要:
- **在Linux上使用主机网络。** 使用已发布的端口,Docker可以重写客户端地址到桥接网关,而来自`172.17.0.1`的警报说每个入侵都来自`172.17.0.1`,这没人能采取行动。在Docker Desktop(macOS / Windows)上,这种重写是不可避免的 - 在那里映射端口,并将源IP视为不可靠。
- **保留卷。** 它们保存SSH主机密钥,诱饵TLS证书,控制台数据库和操作帐户。更改的密钥材料每次重启都会识别盒子是蜜罐,任何人都可以连接两次。
两个容器作为非root用户运行,具有只读根文件系统,丢弃每个功能,并且`no-new-privileges`。端口是默认的非特权端口,因此不需要授予任何权限。
控制台镜像没有shell运行健康检查,因此二进制探针本身 - `wisp-console healthcheck`被设置为镜像的`HEALTHCHECK`,而编排器将使用它来决定控制台是否存活。
## systemd
两个部分的单元都在[`deploy/systemd/`](deploy/systemd/)中,与容器相同的标准进行sandboxing:没有功能,没有可写的但它们自己的状态目录,seccomp过滤器,并且没有路径返回根目录。传感器故意解析敌对输入,因此单元文件不应依赖代码正确。
```bash
sudo install -m 0755 wispd /usr/local/bin/wispd
sudo useradd --system --no-create-home --shell /usr/sbin/nologin wisp
sudo install -m 0644 deploy/systemd/wispd.service /etc/systemd/system/
sudo systemctl enable --now wispd```
`StateDirectory`和`ConfigurationDirectory`创建`/var/lib/wisp`和`/etc/wisp`,并具有正确的所有权,因此没有`chown`步骤。完整的说明,包括控制台和如何绑定特权端口而不授予权限,都在[`deploy/systemd/README.md`](deploy/systemd/README.md)。
## 控制台
一组写入本地文件的传感器并不是一个监控系统。`wisp-console` 是使其成为监控系统的关键组件:传感器通过 HTTPS 将数据发送到控制台,并附带身份验证令牌,所有警报都集中在一个地方。
`wisp-console` 是自行托管的,只需要一个可写入的文件 —— 通过纯 Go 驱动的 SQLite,因此控制台也是一个单独的静态二进制文件。
```bash
go build -o wisp-console ./cmd/wisp-console
./wisp-console sensor add sensor-01
```
这会打印一个令牌,一次性。将其放在传感器的配置文件中:
```yaml
log:
file: events.jsonl
console: true
remote:
url: "https://console.example.com"
token: "wisp_..."
```
然后运行控制台:
```bash
./wisp-console -addr :8000
```
每个传感器都有自己的令牌,**节点名称来自令牌,而不是请求体**。传感器不能声称自己是另一个传感器,因此操作员永远不会被发送到错误的机器。通过以下命令管理它们:
```bash
./wisp-console sensor list
```
```bash
./wisp-console sensor revoke sensor-01
```
重新运行 `sensor add` 命令为现有节点颁发新令牌并使旧令牌失效 —— 这既是轮换也是“我丢失了令牌”。只有令牌的 SHA-256 被存储,因此被盗的控制台数据库不会产生有效的凭证。
为了兼容性,单个共享令牌仍然有效(`-token` 或 `WISP_CONSOLE_TOKEN`),但控制台在启动时会警告:使用共享令牌,节点名称会回落到负载所声称的内容,任何持有者都可以伪造归因于任何传感器的事件。
然后在浏览器中打开控制台。凭证事件(`login_password`、`auth_attempt` 等)被高亮显示;点击传感器、服务或源 IP 地址会过滤视图。
### 登录
UI 界面后面有一个登录功能,并且没有关闭该功能的设置。让摄取功能在令牌后面锁定,但将仪表板打开则不会保护任何东西 —— 仪表板是每个捕获的密码、提示和被盗令牌的最终目的地。
在第一次启动时,控制台会创建一个操作员账户并打印密码一次:
```
created console operator "admin" with password: QOavm-F81rHgpHHQuX-Cqv48
```
从 CLI 管理账户:
```bash
./wisp-console user add alice
```
```bash
./wisp-console user list
```
`user add` 和 `user passwd` 生成密码并打印一次;使用 `-password-stdin` 提供自己的密码(最小 12 个字符):
```bash
printf '%s\n' "$NEW_PASSWORD" | ./wisp-console user passwd alice -password-stdin
```
更改密码会注销该账户持有的所有会话 —— 通常更改密码是因为密码泄露了,而留下旧会话是没有任何意义的。会话默认持续 12 小时(`ui.session_ttl`),存储在数据库中,因此重启不会使所有人退出登录,并且存储为哈希值,因此被盗的数据库不会产生有效的访问权限。
传感器令牌和操作员登录是独立的凭证,因此坐在敌对分段上的传感器可以写入事件但不能执行其他操作。它的令牌不会打开 UI,UI 会话也不会传递事件。
在反向代理后面,设置 `ui.trust_proxy: true` 以便速率限制查看真实的客户端地址而不是代理的地址 —— 否则,一个攻击者的失败登录会锁定所有人。
### TLS
控制台自己终止 TLS —— 不需要反向代理。传感器发送捕获的凭证及其承载令牌,操作员发送密码;这些都不应该在网络中明文传输。
```yaml
tls:
mode: self-signed # off | self-signed | file | acme
cert_file: console-cert.pem
key_file: console-key.pem
domains: ["console.internal"]
```
**`self-signed`** 是大多数控制台的正确选择,它们位于内部网络中,没有公共 DNS 名称。证书生成一次并保存 —— 更改的证书每次重启都不能固定,因此其指纹在启动时打印:
```
TLS: self-signed certificate
SHA-256 51:49:55:A1:64:D0:50:F0:DD:CE:22:40:A2:EA:D3:A6:...
```
将其提供给传感器,以免有人必须使用“跳过验证”:
```yaml
log:
remote:
url: "https://console.internal:8000"
token: "wisp_..."
fingerprint: "51:49:55:A1:..." # 或 ca_file: console-cert.pem
```
具有固定值的传感器拒绝交付给其他人 —— 包括重新签名连接的 TLS 代理。(这并非假设:在运行 Norton Web Shield 的机器上,固定传感器正确地拒绝与主机上的控制台通信,因为它获得的是 Norton 的证书而不是控制台的证书。)`insecure_skip_verify` 存在,被记录为实验室设置,并将每个捕获的凭证发送给响应连接的人。
**`acme`** 从 Let's Encrypt 获取具有公共名称的证书。它需要 `accept_tos: true` —— 代表操作员同意 CA 的条款不是软件的调用。端口 80 是可选的:没有 `http_addr`,颁发使用 TLS-ALPN-01 over 443。设置 `http_addr: ":80"` 以提供 HTTP-01 挑战并将纯 HTTP 重定向到 HTTPS。
**`off`** 仍然存在于已经有代理的部署之前,并且控制台在每次启动时都会大声警告。
### 搜索、分页和导出
事件列表页面显示 100 行,搜索框与 JSON 数据 blob 中的每一列进行匹配 **,包括 JSON 数据 blob** —— 有趣的字符串都存储在其中。搜索 `hunter2` 找到使用它的登录;搜索 `/etc/shadow` 找到询问它的提示;搜索 `10.0.0.9` 找到该地址接触的所有内容。单击传感器、服务或源 IP 地址仍然过滤,并且搜索在已经过滤的范围内缩小,而不是替换它。
导出采用与启动的页面相同的过滤器,以 CSV 或换行符分隔的 JSON —— 与传感器的 `events.jsonl` 形状相同,因此任何已经指向该传感器的内容都可以读取。
```
/export.csv?q=hunter2&hours=168
```
关于 CSV 的一个值得注意的细节:其中的每个字段都是攻击者选择的 —— 用户名、探测路径、提示。 以 `=` 开头的值在电子表格打开文件时被执行为公式,因此精心选择用户名的攻击者可以执行代码以获取分析师阅读导出的机器上的代码执行。 这些值在导出时以撇号为前缀;有效载体仍然可读为证据,只是不能运行。
### 当传感器静默
传感器无法报告自己的死亡。如果有人发现它并停止了它,最后一件事就是它变得沉默了 —— 而沉默的传感器看起来与网络中没有任何事情发生的情况完全一样。 控制台是唯一可以区分这两者的地方:
```yaml
sensors:
silence_after: 30m
check_interval: 1m
```
超过阈值会触发 `sensor_silent` 事件:存储在时间轴中,通过每个通知器推送,并在传感器列表中以红色标记。 返回时触发 `sensor_returned`,因为 3 点钟被唤醒的人不应该猜测它是否仍然关闭。 每个事件仅在转换时报告一次,因此保持关闭的传感器不会每分钟都通知任何人。
这是 wisp 中唯一一个因为没有收到任何东西而触发的警报,而且它是最可能引起关注的警报:以关闭传感器开始的入侵是其他入侵不可见的。
### 保留
如果不干预,数据库会无限增长,其大小由扫描传感器的人决定 —— 控制台的磁盘已满时停止记录入侵。 在 `console.yaml` 中设置策略:
```yaml
retention:
events: 90d # 最大年龄;接受 d 和 w 以及 h/m/s
max_events: 1000000 # 硬上限,无论年龄;最旧的先删除
interval: 1h # 策略应用的频率
```
两个限制同时生效,先到先得。计数上限是年龄无法提供的:一个传感器在持续扫描下会产生数月的事件,在几小时内。 删除的空间返回到文件系统 —— SQLite 重用释放的页面,但从不自行缩小文件,因此控制台在足够大的扫描后重建它。
默认设置是无限期,因为静默丢弃操作员的证据是错误的做法 —— 但控制台在启动时警告,直到设置策略。
交付设计为尽力而为:`Emit` 永远不会阻塞服务协程,当控制台无法访问时,事件队列并最终丢弃。 如果传感器因为报告通道阻塞而停止响应网络,那么这比丢失遥测数据更糟糕 —— 一个挂起的服务是一个可检测的迹象。
### 通知
如果没有人打开的仪表板不是监控。复制 `console.example.yaml` 到 `console.yaml` 以通过 **电子邮件**、**LINE** 或 **webhook** (JSON、Slack、Teams、Discord)发送警报。
两个规则决定发送的内容,两者都很重要:
**只有有意义的种类才会通知。** 提供凭证(`login_password`、`auth_attempt` 等)和陈述意图(`prompt`、`tool_calls`、`command` 等)。 裸连接和版本探测存储但不推送 —— 它们是调查的上下文,而不是唤醒某人到 3 点的理由。
**在可配置的窗口(默认为 15 分钟)内,按 `sensor|service|kind|source-IP` 抑制重复。** 下一个警报在窗口后显示已折叠了多少个:`ssh login_password from 10.0.0.9 (+47 similar suppressed)`。第二个源 IP 地址始终是一个新的警报 —— 抑制永远不要隐藏新的攻击者。
在本地端到端运行中,40 个存储的事件产生了 7 条通知 —— 来自一个源的重复 telnet 暴力破解从 4 条警报折叠成 1 条。 没有这个功能,一个端口扫描会使您的通道静默,而下一个真正的入侵会错过。
LINE 存在是因为在台湾和日本,运营团队实际上在那里。
## 速率限制
一个 Honeypot 每次陌生人触摸它时都会写入一条记录。攻击者找出它是什么后,可以将其反过来:打开端口,传感器会填满磁盘并用交付淹没控制台。 传感器不应该是导致整个舰队监控崩溃的东西。
默认启用限制。所有值都是每分钟的事件数;突发是指一次可以到达多少个:
```yaml
log:
rate_limit:
enabled: true
per_source_per_minute: 60
per_source_burst: 30
high_value_per_minute: 30 # 凭证和陈述意图
high_value_burst: 60
global_per_minute: 600 # 整个传感器,每个源
global_burst: 300
```
三个属性比数字更重要:
- **来自新源的前几个事件始终会落地。** 那就是警报;为了节省磁盘而丢弃它完全是相反的。
- **凭证和提示有自己的预算。** 廉价连接的洪流不能淹没重要的 `login_password` —— 保护列表与控制台通知的列表相同。
- **抑制被报告,而不是沉默。** 截断的日志看起来像安静的网络,而安静与洪流的对立面是应该发生的。 限流源会发出 `rate_limited` 事件并带有计数:
```json
{"service":"http","kind":"rate_limited","src_ip":"127.0.0.1",
"data":{"dropped":92,"duration":"1m2s","kinds":{"probe":92}}}
```
在本地运行中,10 秒内 100 个请求被记录为 8 个事件和 1 个摘要 —— 洪水仍然可见,并且它只占用 9 行而不是 100 行。
按源限制也是保护传感器范围预算的:一个地址泛滥只能花费其自身的津贴,因此它不能让网络的其他部分保持沉默。 源表也有界限,因为攻击者轮换地址是另一种拒绝服务的路线。
## 布局
```
cmd/wispd/ 传感器入口点、服务连接、信号处理
cmd/wisp-console/ 控制台服务器、传感器/用户 CLI、健康检查
internal/config/ 传感器 YAML 配置,带有默认优先加载
internal/event/ 所有服务发出的事件类型
internal/sink/ 控制台 + JSONL 输出、远程交付、速率限制
internal/tlsutil/ 虚假证书,以及传感器如何信任控制台
internal/service/ 服务接口
httpdecoy/ HTTP 形状的诱饵共享的机制
servicetest/ 每个模拟器测试的测试桩
sshsvc/ OpenSSH 通过 x/crypto/ssh 模拟
httpsvc/ 虚假设备管理员登录,纯粹和 TLS 后面
mongosvc/ MongoDB 线协议和 SCRAM 捕获
llmnrsvc/ LLMNR 中毒检测(不是诱饵)
ollamasvc/ 虚假 Ollama 推理服务器
k8ssvc/ Kubernetes apiserver,以及针对它的令牌
kubeletsvc/ kubelet:pod 库存和 pod 内命令捕获
dockersvc/ Docker 引擎 API 和容器逃脱规范
imdssvc/ 云实例元数据,AWS + GCP + Azure
elasticsvc/ 开放的 Elasticsearch 和运行的查询
jenkinssvc/ Jenkins,包括 Groovy 脚本控制台
gitlabsvc/ GitLab,以及被盗的 glpat- 访问令牌
internal/console/ UI、身份验证、保留、TLS 终止
store/ SQLite:事件、传感器、操作员、会话
notify/ 电子邮件、LINE、webhook 和警报去重
```
添加协议意味着一个包实现 `service.Service` 和 `buildServices` 中的一行。
## 开发
```bash
go test ./...
```
每次推送和拉取请求都会运行 CI:`gofmt`、`go vet`、`go mod tidy` 清洁度测试、Linux/macOS/Windows 上的测试、竞态检测器、`govulncheck` 和所有支持目标的跨编译 —— “任何 Go 跨编译的地方” 是本 README 中的声明,因此它被检查而不是留给第一个尝试在树莓派上尝试的人。(它已经赚取了它的价值:Ollama 诱饵的模型大小是无类型常量大于 32 位 `int`,因此传感器不适用于 `linux/arm`。) 两个容器镜像都构建 —— 从未推送 —— 控制台启动以确认其自己的健康检查。
### 发布
标记就是整个过程:
```bash
git tag v0.2.0 && git push origin v0.2.0
```
这再次运行测试(标签可以被推送到 CI 从未看到的提交),为七个平台构建两个二进制文件,发布带有校验和和构建提供证明的 GitHub 版本,并将多架构容器镜像推送到 `ghcr.io`。 带有连字符的标签(如 `v0.3.0-rc1`)被标记为预发布。
每个二进制文件都被标记:`wispd -version` 报告发行版、提交和 Go 版本。来自工作树的构建显示 `dev` —— 没有假装成它不是的发行版。
要检查下载内容是否与声称的构建相同:```bash
sha256sum -c SHA256SUMS --ignore-missing
```
```bash
gh attestation verify wisp_v0.2.0_linux_amd64.tar.gz --repo willysnow/wisp
```
## 操作说明
- **默认端口是无特权的**,因此 `wisp` 以提升权限启动。 将 22 重定向到 2222 在防火墙上,或在 Docker 中映射端口以捕获实际端口上的扫描。
- **保留 `hostkey.pem`。** 每次重启都会更改主机密钥以标识盒子以供任何人两次连接。
- **从不授予访问权限。** 每个凭证都被拒绝,HTTP 服务始终回答“无效用户名或密码” - 从不“没有这样的用户”,这会让攻击者枚举帐户。
- **这是传感器,不是盾牌。** 它检测入侵;它不阻止。 将任何警报视为真正的入侵,直到证明并非如此 - 合法的东西没有任何理由与之通信。
## 路线图
粗略顺序。 对等工作是不 glamorous 但使 `wisp` 可用作演示的东西。
**赶上 OpenCanary **(~6 周集中工作)
- [x] 容易的协议,第一批:TCP 标语、telnet、FTP、TFTP、NTP、redis
- [x] 容易的协议,剩余部分:git、LLMNR、MongoDB、HTTPS
- [ ] 中级协议:MySQL、MSSQL、VNC、SIP、HTTP 代理、SNMP
- [ ] 端口扫描检测(OpenCanary驱动iptables;一个跨平台版本需要pcap)
- [ ] RDP — X.224足以捕获连接;需要NLA来获取凭证
- [ ] 原生SMB2/3,带有NTLMSSP — 无外部Samba。捕获NetNTLMv2哈希和使用的*帐户名*
- [x] 警报功能对齐:电子邮件、webhook、LINE 和警报去重
- [x] Syslog 输出,从传感器和控制台
- [ ] HPFeeds 接收器
**超越它**
- [x] Kubernetes API 诱饵
- [x] MCP 服务器诱饵
- [x] Slack / Teams / Discord / LINE 通知接收器
- [x] 自托管控制台,具有警报去重和通知分发
- [x] 每个传感器的摄取令牌、注册和吊销
- [x] 控制台 UI 身份验证和事件保留
- [x] 控制台搜索、分页、CSV/JSON 导出和传感器静默警报
- [ ] kubelet、Docker 套接字和云 IMDS 诱饵
- [x] 控制台本身的 TLS 终止(自签名、文件或 ACME)
- [x] 传感器端的速率限制,带有报告的抑制
- [ ] TCP/IP 堆栈指纹塑形(击败 `nmap -O` 需要 nfqueue 或 eBPF;用户空间 Go 无法更改 TTL 或窗口大小)
- [ ] **令牌服务** — DNS/HTTP 回调,Word 文档,kubeconfig,MCP 配置文件。诱饵只会看到已经在您网络上的入侵者;令牌随数据一起旅行,并在其结束时触发。两者涵盖了不同的问题,控制台是接收回调的自然选择。
**使其可部署**
- [x] CI:格式、检查、测试在三个平台上、竞态检测器、漏洞检查、交叉编译和容器构建
- [x] 无依赖容器镜像和一个 compose 堆栈
- [x] 发布过程:标记的跨平台二进制文件、校验和、源代码证明和发布的容器镜像
- [x] 沙盒 systemd 单元,用于传感器和控制台
- [x] CONTRIBUTING、SECURITY.md、问题模板
## 贡献
最有价值的贡献是协议模块 — 目前尚存在的第8到第21个模块的空白使得该项目仅能作为演示使用。要了解贡献所需的检查清单和约束条件,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
安全问题请通过[私密漏洞报告][security]渠道进行,切勿公开。公开的检测工具报告会告知攻击者漏洞位置,而此时所有部署仍存在漏洞。[SECURITY.md](SECURITY.md) 文档中明确了哪些问题被视为安全漏洞,哪些不被视为漏洞,因为“蜜罐可以被指纹识别”是一种已知限制而非漏洞。
[security]: https://github.com/willysnow/wisp/security/advisories/new
## 许可
采用BSD-3-Clause许可证,与OpenCanary保持一致 — 部分协议行为源自OpenCanary,但未复制任何代码。具体许可条款请参见`LICENSE`文件,关于代码来源的说明请参见`NOTICE`文件。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器