跳转到正文

客户端 DNS TCP syslog 审计 ​

先编译可选的 dns-syslog feature,可与原有 eBPF/API 编译选项组合使用:

sh
cargo build --release -p honk-core --features dns-syslog

此 feature 默认关闭,native-api 和 native-ui 不会自动启用它。未编入时,开启 DNS 审计会在启动时报错;审计入口为空实现,不创建队列或发送任务。现有发布构建的 能力列表保持不变,需要使用明确启用了此 feature 的构建。

在 global 配置 TCP syslog 接收端:

dae
global {
    syslog_addr: '192.0.2.10:514'
    syslog_dns_enabled: true
    syslog_cee_enabled: true
    syslog_hostname: 'router01'
    syslog_app_name: 'honk'
}

IPv6 写作 [2001:db8::10]:514。审计默认关闭,地址默认空;启用时必须配置数字 单播 IP 和非零端口。地址、发送开关、CEE 模式及 hostname/app_name 修改均需重启。

syslog_hostname 默认空:启动时读取 /proc/sys/kernel/hostname,移除结尾的一个 LF 后校验,读取失败或非法时回退 -。这是运行 honk 的设备名,不是客户端名。 显式 hostname 为1–255字节、不含空格的可打印 ASCII。syslog_app_name 默认 honk, 要求1–48个同类字节。显式空白、控制字符和 Unicode 会被拒绝,不做 trim。 发送端身份仅写入 RFC5424 消息头;JSON 正文不再包含 hostname 和 app_name, 避免接收端解析后出现重复字段。

采集和事件格式 ​

DNS 控制器以及独立 dns.bind 的 UDP/TCP 入口捕获实际客户端、前端处理开始时间和 最终生成的应答。每个请求或 TCP 帧产生一个逻辑事件,包含缓存命中、负应答、可解析 拒绝和失败。上游重试不增加事件,客户端重发分别记录。应答生成前取消时记录 result: "cancelled"、rcode: null。无法解析出问题的报文计数后跳过。开始时间是 审计入口处理时间,不是内核收包时间。

审计独立于 native-api、UI、record_dns_log、512条历史队列和日志级别。 诊断调用、后台刷新及引擎内部解析不记录。仅原样 direct/group TCP/UDP 中继、未由 DNS 控制器处理的53端口流量不在覆盖范围内,不改变原有路由策略。

输出使用固定 RFC5424 头:PRI134(local0/info)、UTC 时间、配置的身份、MSGID dns.query,后接完整的紧凑 JSON 事件。syslog_cee_enabled 默认 false,兼容普通 JSON 接收端;如上例设为 true 时,为支持 CEE 的接收端添加 @cee: 前缀:

text
<134>1 2026-11-02T12:00:00.010000Z router01 honk - dns.query - @cee: {JSON正文}
json
{
  "schema_version": 1,
  "event": "dns.query",
  "message": "DNS 192.168.1.100 example.com. A IN: NOERROR answers=1",
  "event_id": "process-uuid:1",
  "query_time": "2026-11-02T12:00:00.000000Z",
  "completed_at": "2026-11-02T12:00:00.010000Z",
  "duration_ms": 10.0,
  "upstream": "default",
  "client_ip": "192.168.1.100",
  "client_port": 53120,
  "transport": "udp",
  "domain": "example.com.",
  "qtype": "A",
  "qclass": "IN",
  "result": "response",
  "rcode": "NOERROR",
  "response_truncated": false,
  "answers": [
    {"name":"example.com.","type":"A","class":"IN","ttl":60,"data":"203.0.113.20"}
  ],
  "answers_omitted": null,
  "answer_ips": ["203.0.113.20"],
  "answer_cnames": [],
  "answer_types": ["A"]
}

duration_ms 是数字类型的处理耗时,单位毫秒,截取到微秒精度。单调时钟从审计 准入开始计时,在最终响应准备完成或请求取消时固定,排除响应审计数据复制、向客户端 发送响应及后台日志排队。UTC 时间字段仍使用系统时钟,不通过两个时间戳相减计时。 upstream 取最终结果的上游配置名称;正常缓存和过期缓存应答统一写 "cache"。 本地答案、拒绝、取消等没有上游元数据的结果写 null。不输出最初上游或重查询历史。

transport 描述客户端 DNS 方式,审计输出始终使用 TCP。完整答案保留名称、类型、 类别、TTL 和内容,未知类型用 RFC3597 文本。honk 生成按首次出现顺序去重的数组: answer_ips 提取 A/AAAA data,answer_cnames 提取 CNAME 目标,answer_types 提取全部类型,无匹配时输出空数组。不删除或改写原始答案。应答校验只检查记录边界, 不检查每条记录内容,已发送给客户端的应答仍可能无法渲染:此时事件保留其他字段, answers 为 null、数组为空,answers_omitted 为 "invalid"(投影超过事件上限时为 "budget");其他情况 answers_omitted 为 null。TC 描述实际客户端应答, 可能受客户端 UDP 大小限制。捕获在应答发送前,不证明客户端已收到。进程 UUID 重启后变化,序号标识生成的事件,不为准入失败编号,也不证明无丢失。

分帧与组批 ​

使用 RFC6587 octet counting:完整 RFC5424 消息的十进制字节数、一个 ASCII 空格、消息内容。长度包含消息头、CEE 前缀和 JSON,不包含 前缀本身,按 UTF-8 字节计数而非字符,不添加换行或 NUL。一批拼接多条独立分帧事件:

text
<长度1> <消息头和JSON1><长度2> <消息头和JSON2>

接收端必须支持长度前缀,不能直接使用 HTTP JSON Lines 接口。不发送 UDP 或 应用层分片;超过8192字节的事件在4MiB上限内仍为完整一帧。重复字段名、展开名称、 检索数组、长 TXT 和二进制或未知记录都会增加体积。

每个目标一个异步任务,按需建立并复用一条 TCP 连接。组批目标128KiB,或从首条 已准备消息起满1秒刷新;后续到达不重置期限。单条超过批量目标时单独完整发送。 完整 RFC5424 消息上限4MiB:答案分配前检查有限投影预算,序列化也有上限。 超过投影预算的答案按上文省略;仍超过消息上限的事件整体丢弃并计数,不静默截断答案。 这些是起始参数,不是吞吐保证。

准入与故障 ​

DNS 路径只提交不可变报文,不等待网络。最多允许1024个捕获中或待处理事件,原始 请求/应答、复制的上游名称及条目核算限制8MiB。应答字节准入不等待,失败丢弃整个事件,不误记取消。 队列满丢弃新事件并计数,不拖慢 DNS。关闭功能不复制报文、不启动任务。

8MiB 是原始数据准入限制,不是总内存。后台额外持有有界投影/JSON、分帧复制、 普通不超过128KiB的批次或一条不超过4MiB的事件,以及刷新时最多一条已准备的下一帧。 大批次容量使用后释放。

套接字 connect 前设置实际 global.so_mark_from_dae,开启 TCP_NODELAY。 不解析接收端域名、不走代理,mark 失败不允许无标记回退。连接和批次写入超时3秒。 连接失败保留有界批次、停止消费队列,以250ms到30秒指数退避重连。断线时无法保证 1秒刷新;仅整批写入成功才重置退避,连接成功本身不重置。

写入错误或超时可能已发送部分批次,因此丢弃不确定批次、关闭连接,不重放。 后续排队事件使用下一条连接。不承诺恰好一次、应用层确认或接收端持久化证明。 写入成功计为 submitted,不等于确认入库。

关闭立即取消连接、写入和退避,关闭准入并丢弃待发送内容;1秒是等待/终止任务上限, 不是最后一次刷新的宽限期。owner Drop 终止任务,重载不重复创建发送器。 关闭时报告丢弃、无效、提交事件及连接/写入失败。接收端故障开始时记录一次带错误的 WARN,之后批次写入成功时记录 INFO,其间重试为 DEBUG;队列上限导致的丢弃最多每 10秒 WARN 一次,无法编码的事件记录为 DEBUG。本地不落盘,远端负责留存、轮转和检索。

展示消息 ​

完整 JSON 事件包含非空 message 摘要,描述规范化客户端 IP、 查询问题、应答状态(或 cancelled)和答案数量。已有结构化字段和完整答案继续用于 审计检索;摘要同样计入整条消息的字节上限。

启用 syslog_cee_enabled: true 后,VictoriaLogs 的 syslog 接收器展开 CEE JSON, 将 message 映射到 _msg,用于日志 列表展示。 缺少该字段的旧事件可能显示默认的缺失消息占位提示,其他字段仍已保存。 参见 VictoriaLogs syslog 及消息字段。

固定格式与配置变更 ​

消息头布局和 JSON 字段结构固定,见上文。不提供消息模板、JSON 模板、标签、格式 版本或 CEF 模式。序列化在后台发送任务中执行,普通配置重载既不改变事件格式,也不 重启 TCP 发送器。上述五项设置在审计关闭时修改同样需要重启。

从较早的 fork 构建迁移 ​

原先从 JSON 正文读取 hostname 或 app_name 的接收端,应改为使用解析后的 RFC5424 头部对应字段。已有的历史日志不受影响。

删除 syslog_dns_template 和 syslog_dns_json。dae 和结构化配置均按旧键是否出现 进行拒绝,空字符串、false 和 null 也不能保留。若需保持上一版固定 CEE 输出,显式 设置 syslog_cee_enabled: true;否则默认发送普通 JSON。开启审计前还需确保二进制 已编入 dns-syslog feature。


来源:honk 上游文档,采用 GPL-3.0-only 许可证。