启动事件
进程与监听端口就绪
记录版本号、加载的配置文件路径与监听端口,用于确认实例是否正常起来。
快连VPN 采用白名单而非黑名单:只有被显式列出的事件类型才允许写入日志,其余内容一律不记录。白名单内的状态事件共四类——启动、握手、规则加载、异常。
进程与监听端口就绪
记录版本号、加载的配置文件路径与监听端口,用于确认实例是否正常起来。
上游链路协商结果
记录使用哪个协议出口与握手成功或失败,不记录握手过程中使用的凭据内容。
规则条数与语法校验
记录规则文件版本、条目数量与语法校验结果,不记录规则命中的具体目标。
错误码与发生阶段
记录原因码与发生阶段,用于定位失败环节,不附带请求内容或响应内容。
以下数据无论连接是否成功、无论是否命中分流规则,都不会出现在日志中,也不会写入任何本地文件:
| 数据类别 | 是否进入日志 | 说明 |
|---|---|---|
| 访问域名 | 否 | 仅在连接建立与传输期间存在于内存,连接结束即释放 |
| 目标 IP | 否 | 不写入日志,不落盘,不参与审计导出 |
| 连接时间 | 否 | 不记录每条连接的起止时刻,避免通过时间序列反推访问行为 |
| 流量大小 | 否 | 不按连接统计上下行字节数 |
| 账号标识 | 否 | 不记录用户、订阅或设备标识,日志中不存在可用于关联到自然人的字段 |
查看白名单级别日志时使用下列命令。日志输出到 stderr 与容器日志驱动之间,默认不在容器内生成持久化日志文件。
终端 · 查看白名单级别日志# 只输出白名单内的状态事件(启动 / 握手 / 规则加载 / 异常)
docker logs --tail 200 kl-proxy 2>&1 | grep '\[state\]'
# 仅看异常与原因码,用于排障
docker logs kl-proxy 2>&1 | grep '\[error\]'
# 确认日志中不存在访问明细类字段:应无任何匹配输出
docker logs kl-proxy 2>&1 | grep -E '(host=|dest=|bytes=|uid=)'
当合规要求提供可核查证据时,正确做法是导出规则决策元数据(规则 ID、命中维度、动作、时间戳),而不是会话内容。前者解释分流行为且不含访问明细,后者会破坏零日志前提。两者的边界与导出方式见 命中审计。
崩溃转储(core dump)会把进程内存整体写入磁盘,其中包含会话密钥、订阅凭据以及正在传输的流量内容。快连镜像在构建阶段即关闭这一路径。
ulimit -c 0,进程即使异常终止也不会生成 core 文件。/proc/sys/kernel/core_pattern 指向不可写路径(容器内为 /dev/null),即使宿主机策略允许转储,也没有可写入的位置。部署完成后应当验证这两项设置确实生效,而不是默认相信镜像声明:
终端 · 验证崩溃转储已关闭# 查看容器内的 core 大小限制,输出 0 即为生效
docker exec kl-proxy sh -c 'ulimit -c'
# 输出示例:
# 0
# 确认 core_pattern 指向不可写路径
docker exec kl-proxy sh -c 'cat /proc/sys/kernel/core_pattern'
# 确认容器未挂载任何持久卷
docker inspect kl-proxy --format '{{json .Mounts}}'
若确实需要完整调用栈,使用排障镜像在隔离环境复现问题,不要在生产实例上临时放开 core 大小限制。排障镜像的差异见 镜像矩阵。
密钥在内存加密区域中完成加载、使用与销毁三个阶段,全程不落盘、不进入 swap。理解这三个阶段,就知道了密钥在任意时刻可能出现在哪些位置。
为什么在这一步加载
凭据在进程启动阶段一次性读取并解析,写入内存加密区域。读取完成后不再保留原始明文副本,配置来源也不会被二次读取。容器重建即产生一份全新的内存状态,不继承上一次运行的密钥。
为什么这样存放
密钥与订阅凭据在使用期间常驻内存加密区域,不写入持久卷、不写入规则配置文件、也不进入 swap。运行时若需要临时派生会话密钥,仅在内存中完成,用毕随会话一并销毁。
为什么要显式覆写
会话结束即对内存中的密钥材料做覆写处理,使其不可继续使用。进程退出时不保留可复用的历史副本,容器删除后内存状态随之释放,不存在可供事后提取的残留。
| 密钥类型 | 存储位置 | 生命周期 | 销毁时机 |
|---|---|---|---|
| 会话密钥 | 内存加密区 | 单次会话 | 会话结束即销毁 |
| 订阅凭据 | 内存加密区,不写持久卷 | 进程存活期 | 进程退出即销毁 |
| 设备标识 | 内存,容器重建即失效 | 容器生命周期 | 容器删除即销毁 |
| 规则配置 | 只读挂载的 YAML | 配置周期 | 容器停止后卸载 |
规则配置只描述分流规则与出口定义,通过只读方式挂载,内容中不包含订阅地址等凭据。把凭据写进规则文件等于把它变成一份可被备份、可被版本管理、可被读取的明文文件,与内存加密的前提直接冲突。凭据的传入方式见 部署文档。
下表是本页的核心结论:每一类数据是否落盘、是否进入日志、是否内存加密,三项属性分别独立成立。凭据与会话密钥三项全为否或全为是,是零日志设计的落点。
| 数据类别 | 是否落盘 | 是否进日志 | 是否内存加密 |
|---|---|---|---|
| 订阅地址 / 凭据 | 否 | 否 | 是 |
| 会话密钥 | 否 | 否 | 是 |
| 访问域名与目标 IP | 否 | 否 | 仅内存 |
| 连接时间与流量大小 | 否 | 否 | 仅内存 |
| 规则命中决策元数据 | 可选 | 可选 | 否 |
| 运行状态(启动 / 重连 / 错误码) | 可选 | 是 | 否 |
「可选」表示由部署方决定是否开启:默认运行时不落盘,若确有审计或留存需求,可显式开启对应的导出目标,但开启范围仅限决策元数据与运行状态,不覆盖访问明细。分流引擎产生的决策记录字段见 命中审计。
零日志与可审计并不冲突,因为两者约束的不是同一类数据:零日志排除的是会话内容,可审计依赖的是规则决策元数据。
审计导出内容仅包含四个字段:规则 ID、命中维度、动作、时间戳。不包含访问域名、目标 IP、会话内容与账号标识,也不包含任何可用于还原用户身份的字段。这四个字段回答的是「这条连接依据哪条规则、走了哪个出口」,不回答「用户访问了什么」——前者是配置行为的证据,后者才是访问行为。
会话内容
访问域名、目标 IP、连接时间、流量大小、账号标识一律不落盘、不进日志。这一侧回答「系统记住了什么」,答案是:只记住四类状态事件。
决策元数据
规则 ID、命中维度、动作、时间戳。这一侧回答「系统为什么这样转发」,答案来自规则文件本身,而不是来自对用户访问行为的记录。
配置行为的证据
决策元数据的取值只由规则文件与连接属性决定,与访问的具体内容无关。因此导出它不会扩大任何隐私暴露面。
合规评审通常需要三类材料,均可在本页口径下提供:日志白名单范围说明、崩溃转储关闭的验证方式与输出、密钥生命周期表(即表 2)。需要提交正式材料或对适用范围有疑问时,通过 备案与联系方式列出的渠道联系,材料中请勿附带任何订阅地址等凭据。
不会。访问域名与目标 IP 不进入日志,也不落盘,仅在连接建立与传输期间存在于内存中,连接结束即释放。日志只有启动、握手、规则加载、异常这四类状态事件。排障需要的信息以原因码形式输出,不含具体访问内容。
不会明显影响。镜像内已设置 ulimit -c 0 并屏蔽 core dump 输出路径,进程崩溃时不会把内存中的密钥与流量信息写入磁盘。排障需要的最小信息以原因码形式输出到 stderr,足以定位绝大多数启动与握手阶段的问题;需要完整调用栈时应使用排障镜像在隔离环境复现,而不是在生产开启转储。
不矛盾。两者依赖的不是同一类数据:零日志约束的是会话内容,审计依赖的是规则决策元数据。审计导出只含规则 ID、命中维度、动作与时间戳四个字段,不含访问域名、目标 IP、会话内容与账号标识,因此可以在不记录任何访问明细的前提下解释每条流量的出口选择。
不会。订阅地址与凭据只在进程启动时读取,随后常驻内存加密区域,不写入持久卷,也不写入规则文件。进程退出时该区域被覆写,不保留可复用的历史副本。规则配置文件本身只保存分流规则与出口定义,不包含任何凭据。