保留操作记录、不记录秘密正文的审计策略
目标
在个人 VM 上真实的 k3s 中依次完成:合成秘密暴露 → 恢复到 Metadata → 用 None 制造证据缺失 → 最终恢复。
为什么重要
仅仅存在策略并不会自然产生安全的审计追踪。必须同时检查实际请求是否留下记录,以及正文是否未被复制。本练习用于训练 CNPE 审计追踪能力的一部分,并不表示复制正式考试题目,也不涵盖整个安全领域。需要具备 Kubernetes 资源、RBAC、JSON、jq 和 systemd 基础。
已准备内容
环境会准备 Ubuntu 24.04 个人 VM、k3s v1.36.4+k3s1、cnpe-audit Namespace 与合成 canary,以及合成 Secret 的详细审计记录。首次准备可能需要数分钟。所有相对路径均位于 /root/cnpe-audit 下。drop-in 的完整路径是 /etc/rancher/k3s/config.yaml.d/95-cnpe-audit.yaml。它是 API 服务器策略文件,不是可用 kubectl apply 应用的资源。
本练习时长为 75 分钟,请在默认 60 分钟会话中使用+时间延长。会话结束后文件和 VM 都会消失。检查是否暴露值后,请另行保存所需报告。systemctl restart 只能在此 VM 内使用。不要操作宿主集群或他人的会话,也不要放入真实凭据或个人信息。
步骤
- 检查自己 VM 的 API 与审计环境——运行 kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml get nodes,确认这是只有当前主机一个节点的集群。Namespace cnpe-audit 专用于本练习。环境已启用对合成 Secret 的详细采集。不要带入其他集群的 kubeconfig 或真实秘密。
- 报告是否暴露,而不是报告具体值——读取准备好的 unsafe-case.json 和 unsafe-events.jsonl,编写 unsafe-report.json。requests 是对应 Secret 的 ResponseComplete 事件数;body_present 表示是否存在 requestObject 或 responseObject 键;marker_exposed 表示 case 中 marker 的原文或 base64 是否暴露;control_present 表示是否存在 case.control_uri 的 200 对照记录。只有四个 Secret 请求均存在、没有正文或标记且存在对照记录时,safe_to_accept 才为 true。不要把原始秘密值复制到报告中。
- 编写去除正文但保留追踪的策略——在 safe-policy.json 中写入 apiVersion audit.k8s.io/v1、kind Policy、omitStages [RequestReceived]。rules 包含两条:第一条为 level Metadata、namespaces [cnpe-audit]、resources [{group: 空字符串, resources: [secrets]}];第二条是不带条件的 level Metadata。本练习使用这一受限策略约定,并非通用策略编辑器。
- 将策略文件接入运行中的 API——用 JSON 语法编写 95-cnpe-audit.yaml。唯一键 kube-apiserver-arg+ 的列表中加入 audit-policy-file=/root/cnpe-audit/safe-policy.json、audit-log-path=/root/cnpe-audit/runtime.jsonl、audit-log-mode=blocking、audit-log-maxsize=5、audit-log-maxbackup=1、audit-log-maxage=1。重启个人 VM 内的 k3s,并确认 /readyz 返回 ok。评分还会检查新合成 canary 查询的实际审计级别。
- 实际发送创建、读取、拒绝和删除请求——按下方参考中的采集命令格式,将阶段参数选为 safe 后执行。采集器会创建本次 UUID 的合成 Secret,执行管理员读取、无权限模拟用户读取、删除,并另外发出 /version 对照请求。必须生成 safe-case.json、safe-events.jsonl、safe-origin.jsonl。四个请求的状态应为 201、200、403、200,且 Metadata 中不得有正文。
- 创建不会让空记录通过的摘要——从 safe-case.json 和 safe-events.jsonl 计算与第 2 步相同的五个字段,并保存为 safe-report.json。同时保留采集时的原始摘录和分析文件。该报告只是此采集器受限场景的摘要,不是可批准任意审计日志的通用安全门禁。
- 将被 None 隐藏的事件判定为失败——创建与第 3 步策略相同的 none-policy.json,但仅把第一条 Secret 规则改为 None。只把 drop-in 中的策略路径改为该文件,重启个人 k3s、确认就绪,再运行 collect none。在 none-report.json 中计算同样五个字段。Secret 记录应为 0,对照记录应存在,safe_to_accept 必须为 false。完全关闭审计功能与这个反例不同。
- 恢复安全策略并留下新证据——把 drop-in 改回 safe-policy.json,重启个人 k3s。运行 collect final 生成新请求,并计算 final-report.json。不要复制旧的 safe 报告。评分会同时检查新请求和当前 API 的 canary 查询。完成后不要开始其他练习,直接结束此个人会话即可。
参考
采集命令格式为 python3 /opt/fixtures/cnpe-audit-lab.py collect <단계>。
将 <단계> 替换为任务指定的 safe、none、final 之一。
collect 是会显式创建和删除合成 Secret 的采集命令。check 会读取文件和当前 API,不会修改 canary。若采集失败,它会先更新请求标识符,避免把旧证据误认成新请求。由于另行保存 origin 摘录,即使原始日志轮转,之前阶段仍可重新评分。这并不是防止学生 root 篡改、中央采集、不可变存储、断电或保护全部敏感资源的证据。
恢复最终安全策略后再执行完整评分。第 7 步启用 None 期间,要求当前安全状态的第 4 步失败是正常现象。
官方文档
- https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/
- https://docs.k3s.io/installation/configuration
- https://training.linuxfoundation.org/certification/certified-cloud-native-platform-engineer-cnpe/
检查自己 VM 的 API 与审计环境
运行 kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml get nodes,确认这是只有当前主机一个节点的集群。Namespace cnpe-audit 专用于本练习。环境已启用对合成 Secret 的详细采集。不要带入其他集群的 kubeconfig 或真实秘密。
应用 Pod 的日志与 API 服务器审计记录并不相同。
报告是否暴露,而不是报告具体值
读取准备好的 unsafe-case.json 和 unsafe-events.jsonl,编写 unsafe-report.json。requests 是对应 Secret 的 ResponseComplete 事件数;body_present 表示是否存在 requestObject 或 responseObject 键;marker_exposed 表示 case 中 marker 的原文或 base64 是否暴露;control_present 表示是否存在 case.control_uri 的 200 对照记录。只有四个 Secret 请求均存在、没有正文或标记且存在对照记录时,safe_to_accept 才为 true。不要把原始秘密值复制到报告中。
存在 unsafe 的详细事件并不意味着安全。不要把 False 保存为字符串。
编写去除正文但保留追踪的策略
在 safe-policy.json 中写入 apiVersion audit.k8s.io/v1、kind Policy、omitStages [RequestReceived]。rules 包含两条:第一条为 level Metadata、namespaces [cnpe-audit]、resources [{group: 空字符串, resources: [secrets]}];第二条是不带条件的 level Metadata。本练习使用这一受限策略约定,并非通用策略编辑器。
第一条匹配规则决定采集级别。若把所有记录都改为 None,连谁读取过也会丢失。
将策略文件接入运行中的 API
用 JSON 语法编写 95-cnpe-audit.yaml。唯一键 kube-apiserver-arg+ 的列表中加入 audit-policy-file=/root/cnpe-audit/safe-policy.json、audit-log-path=/root/cnpe-audit/runtime.jsonl、audit-log-mode=blocking、audit-log-maxsize=5、audit-log-maxbackup=1、audit-log-maxage=1。重启个人 VM 内的 k3s,并确认 /readyz 返回 ok。评分还会检查新合成 canary 查询的实际审计级别。
若没有 +,可能覆盖其他 drop-in 中的现有列表。不要把策略 YAML 当作 Kubernetes 资源 apply。
实际发送创建、读取、拒绝和删除请求
按下方参考中的采集命令格式,将阶段参数选为 safe 后执行。采集器会创建本次 UUID 的合成 Secret,执行管理员读取、无权限模拟用户读取、删除,并另外发出 /version 对照请求。必须生成 safe-case.json、safe-events.jsonl、safe-origin.jsonl。四个请求的状态应为 201、200、403、200,且 Metadata 中不得有正文。
如果只把文件改为安全策略而不重启,实际采集级别不会改变,因此会失败。
创建不会让空记录通过的摘要
从 safe-case.json 和 safe-events.jsonl 计算与第 2 步相同的五个字段,并保存为 safe-report.json。同时保留采集时的原始摘录和分析文件。该报告只是此采集器受限场景的摘要,不是可批准任意审计日志的通用安全门禁。
首先要确认四个请求确实存在。不要只检查没有正文这一项。
将被 None 隐藏的事件判定为失败
创建与第 3 步策略相同的 none-policy.json,但仅把第一条 Secret 规则改为 None。只把 drop-in 中的策略路径改为该文件,重启个人 k3s、确认就绪,再运行 collect none。在 none-report.json 中计算同样五个字段。Secret 记录应为 0,对照记录应存在,safe_to_accept 必须为 false。完全关闭审计功能与这个反例不同。
成功应用 None 策略并不代表满足了安全审计要求。
恢复安全策略并留下新证据
把 drop-in 改回 safe-policy.json,重启个人 k3s。运行 collect final 生成新请求,并计算 final-report.json。不要复制旧的 safe 报告。评分会同时检查新请求和当前 API 的 canary 查询。完成后不要开始其他练习,直接结束此个人会话即可。
仅凭过去安全的日志,无法证明当前正在运行的配置。