输入一个 IPv4 地址,把我们自己的告警 / 蜜罐 / 处置留痕里跟它有关的记录摊开: 首末出现时间、事件数、行为类型、碰过哪些蜜罐端口、试过的口令结构、GeoIP 与封禁状态。 它不是全网情报库 —— 只回答「这个地址在这台机器上留下了什么」; 任何一项读不到就写 读不到(原因)—— 不补数,不按网段或 ASN 猜。
只接受 IPv4 字面量(四段十进制,每段 0–255)。IPv6 / 域名 / 带端口 / 网段写法一律不查 ——
后端会直接 400,页面也不替它"猜一个最像的"。内网与保留地址同样拒答(原因见最后一块)。
查询会写进地址栏的 ?q=,可以直接把这一页的链接发给别人复核。
还没查。上面输入一个 IPv4 后回车(或点「查询」)—— 结果卡会逐项标出哪个数读到了、哪个读不到、以及读不到的原因。
| 事件数 events | 精确值,来自索引聚合(track_total_hits),窗口默认 最近 168 小时(7 天)。
更早的记录这个端点读不到(不是"没发生")—— 窗口外的历史不会出现在任何一项里。 |
|---|---|
| firstSeen / lastSeen | 窗口内最早 / 最晚的一条记录的时间戳,同样来自索引聚合的 min/max,是精确值。 只在这个 IP 的窗口内有记录时才给;没有就 null。 |
| byKind / ports / credStructure | 只基于最近取回的样本(默认最多 300 条,见结果卡里
sources.indexer.sampled)。样本被截断时
sources.indexer.truncated=true,页面会把"这只是样本"写在卡片上 ——
样本里的条数不是总量,别拿它跟 events 对齐。 |
| credStructure | 只给结构:"6 位·纯数字·弱口令"这种。后端从蜜罐日志里抽出
用户名 / 口令 之后立刻换成结构描述,
原文字符一个都不进响应体(出关闸门还会再隐一遍,双重保险)。 |
| geo | GeoIP 库的查询结果(国家 ISO2 + ASN + AS 组织)。库里查不到、或只有 ASN 没有国家, 就写 null 并在 notes 里说明 —— 不按网段或经验补一个国家。 |
| banned | 应用侧处置留痕(data/blocked-ips.json):封过几次、最后一次是什么时候、
现在还封着没有。留痕文件读不到时 banned=null,而不是
count=0 —— "读不到"与"没封过"是两件事。 |
| 为什么会有 null | 三类:数据源没接入(NOT_WIRED)、接入但这次读失败(QUERY_FAILED /
UNREADABLE)、源里确实没有这个地址的记录(NOT_FOUND)。
三种都会写进 notes[] 与 sources.*.status,页面逐条贴出来。 |
一句话口径:宁可少给一个数,也不给一个看起来像真的数。 缺失一律 null + 原因;有数字就说明它是精确值还是样本。
公开:这个 IP 的首末时间、事件总数、行为类型计数、碰过的端口、口令结构、
GeoIP、封禁计数与状态、以及每个源接没接上。
不公开:任何口令原文、应用侧日志原文、防护规则的正则与阈值、别的来源 IP 的数据
(同一个响应里只有 q 这一个地址的记录)。
拒答:非 IPv4 字面量;内网 / 保留 / 链路地址(10/8、192.168/16、
172.16/12、127/8、169.254/16、100.64/10 等)。
后者在公开口径里统一写作「内网/未知」,多个不同的内网地址会共用同一个输入 ——
按 IP 查只会给出误导性的答案,所以直接 400。
不做的:不封禁、不触发任何处置动作、不写任何文件;这个端点是一次纯读。
同一套口径问「现在有什么告警」看 /alerts/; 看「哪个来源的故事完整」看 /status/; 端点清单看 /api/。
# ① 查一个有记录的地址(把 45.155.205.233 换成你想查的) curl -s 'https://demo.xiaokeqing.cn/api/public/ip?q=45.155.205.233' | head -c 600 # ② 对照:非法 IPv4 → 400 + 机读 reason(不是 500,也不是空对象) curl -s -o /dev/null -w '%{http_code}\n' 'https://demo.xiaokeqing.cn/api/public/ip?q=999.1.1.1' # ③ 对照:内网地址同样 400(不拿它去查内网主机的活动) curl -s -o /dev/null -w '%{http_code}\n' 'https://demo.xiaokeqing.cn/api/public/ip?q=10.0.0.1'
响应外层统一是 {code, message, data};缺失项的 data.* 是 null,
原因在 data.notes[] 里逐条写着 —— 页面上的「读不到(原因)」就是它的原文。