审计通知下来那天,团队先做的不是整改,是翻记录:翻群聊、翻工单,把三个月变更凑成一叠截图。交上去,审计员问:"这条生产库的删除操作谁批的?"没人答得上来——有人批过,批在私聊里。
这就是起点:审计要的不是"你当时做对了",是"你现在能证明当时做对了"。这句话决定留痕存哪、审批怎么设、证据何时生成。
1. 可证明性的三个工程后果
团队的回答通常是"有评审""只有几个人有权限""老张会看";审计要的是证据在哪、能否被第三方复核、权限清单与授予时间、控制点是否制度化。由此三条后果:证据须在动作发生时产生,事后重建只算说明;证据要独立于当事人,由系统而非操作者生成;证据要能被外人读懂。第三条最常被忽略——工单写"按上次方案处理",自己人懂,审计不懂,等于没有。
2. 三类留痕与采集点
| 类别 | 回答什么 | 采集点 | 典型失效 |
|---|---|---|---|
| 变更留痕 | 谁在何时改了什么 | CI/CD、IaC 仓库、制品库 | 手工热修绕过流水线 |
| 访问留痕 | 谁碰过哪些主机与数据 | 网关、堡垒机、DB 审计 | 共享账号无法归因 |
| 决策留痕 | 为什么这么改、谁同意 | 评审系统、变更单、ADR | 决策在会议室,无记录 |
三类价值不对称:变更是最易自动化的,访问最易被共享账号破坏,决策最易被跳过——而它是唯一能回答"为什么"的材料。
把三类事件统一成一种结构,取证不用写三套解析:
{"event_id":"uuid","ts":"RFC3339 带时区","actor":"主体标识,禁共享账号",
"action":"deploy|exec|grant|approve","target":"服务+环境+资源",
"decision_ref":"变更单/审批实例 ID","artifact_digest":"sha256:…","source":"pipeline|bastion"}
decision_ref 把"动作"与"授权"缝在一起;缺了它只能证明有人做了,不能证明有人批了。
3. 审批的形式与实质
| 形态 | 拦住的风险 | 副作用 |
|---|---|---|
| 串行三级 | 大型不可逆变更 | 后两级基本复述第一级结论 |
| 并签(两人同时同意) | 单人滥权 | 成本翻倍,只对极少操作值得 |
审批链越长不等于风险越低:每加一级,通过率上升、单级审阅变浅。判断一级有无实质只看三条——有上下文、有独立否决权、为错误批准担责。缺一条就是盖章。
branch_protection:
main:
required_approvals: 2
dismiss_stale_approvals: true # 新提交使旧批准失效,防批完再塞代码
require_last_push_approval: true # 最后推送者不能是自己
这两项是把形式审批变成实质审批的最低成本手段:前者防批完再塞代码,后者防自己批自己。
4. 证据的时效性
| 证据类型 | 事后可重建 | 审计价值 |
|---|---|---|
| 流水线记录、制品签名 | 不可重建 | 高,带时间戳可自证 |
| 工单与审批实例 | 不可重建 | 高 |
| 聊天截图 | 可伪造、可删改 | 低 |
| 事后补录的台账 | 时间戳不可信 | 接近零,可能成不利证据 |
事后补的证据两处硬伤:时间戳不可信(补录≠发生)、独立性丧失(被审计方单方生成)。所以重心不在整理材料,在让材料自动长出来。
要让时间可被第三方验证,需外部时间源(openssl ts):
openssl ts -query -data evidence.sha256 -sha256 -cert -out evidence.tsq
curl -sS -H 'Content-Type: application/timestamp-query' \
--data-binary @evidence.tsq https://TSA_ENDPOINT -o evidence.tsr
openssl ts -verify -in evidence.tsr -data evidence.sha256 -CAfile tsa-ca.pem
ts -verify 通过,等于"该包在某时刻已存在且此后未改"。参数名按 OpenSSL 3.x 核对。
5. 最常被问的三件事与系统缺口
| 要求 | 系统天然缺口 | 补法 |
|---|---|---|
| 谁改的 | CI 用个人 token、共享部署密钥 | 令牌绑流水线身份,人只出现在触发记录 |
| 改了什么 | 只记"部署成功",不记制品摘要 | 每次部署落 digest 与 git sha |
| 谁批的 | 审批在 IM 完成,系统无实例 | 审批进 PR/工单,IM 只通知 |
第三行是多数团队实况:审批真实发生,但发生在聊天工具里,于是在审计视角下"没有发生"。另外"部署成功"≠"全量生效",灰度下要记录后者。这不是流程松散,是采集点选错了。
6. 自动化证据生成
收益不在省事,在于把一次性、集中式、由人组织的取证,变成常态、由系统产出的查询:审计期从两周压到半天,不依赖任何人的记忆。
| 证据 | 生成方式 | 保留期量级 |
|---|---|---|
| 制品来源证明 | 构建时生成 attestation/SBOM | 与制品同寿命 |
syft dir:. -o spdx-json > sbom.json
cosign attest --predicate sbom.json --type spdxjson "$IMAGE_DIGEST"
tar czf evidence.tgz sbom.json pipeline.log approval.json # 证据随制品归档
要点是证据跟着制品走:工单会归档清理,制品摘要不会。
7. 分离职责在小团队的折中
SoD 的本质不是"两个人",是让同一人无法同时制造问题与掩盖问题。人数不够硬套双人审批,只会互相盖章。
| 规模 | 可行方案 | 残余风险 |
|---|---|---|
| 1–3 人 | 事后可见性:高危操作强制通知 + 周复核 | 事后才发现 |
| 4–8 人 | 交叉审批(改的人不批) | 熟人社会,否决成本高 |
| 9–20 人 | 领域 owner + 平台强制门禁 | 门禁成瓶颈 |
务实解是用可见性替换分离:拦不住一个人做完全程,就保证动作会被别人看见,且有低成本回退。
8. 三条设计原则与最小权限
| 原则 | 技术手段 | 失效点 |
|---|---|---|
| 不可篡改 | 追加写日志、对象锁、哈希链 | 有删权的人能删 |
| 可追溯 | 变更单→流水线→制品→部署→日志 全链 ID | ID 在某段断 |
| 保留期 | 分层存储 + 生命周期策略 | 短于审计周期 |
保留期最易被成本压力削掉:追溯按年计、存储按天计费,冲突要用分层解决,而非审计前临时导出。
落地手段是对象存储的版本化 + 对象锁(字段如 object_lock_enabled、default_retention{mode,days}):COMPLIANCE 连 root 也删不掉,这是它唯一的价值;代价是设错改不回来,先用 GOVERNANCE 演练。
权限面越小,要留痕的面越小。全域读权限的运维账号会把访问留痕退化成"他可能看过任何东西"——审计无法证否,只能记风险。
JIT 是性价比拐点:把长期权限变成"有起止时间的授权事件",天然带审批人、理由、有效期。代价是故障时多一步,故须留一条预授权应急通道,其使用被重点留痕。
9. 控制项翻译与成本分层
合规文档给控制项("生产变更应实施授权与记录"),工程要可执行检查,中间必须有一层映射,否则控制项永远停在文档里。
| 控制项 | 技术手段 | 证据 | 自动化 |
|---|---|---|---|
| 变更需授权 | 分支保护 + 审批实例 | PR 审批记录 | 全自动 |
| 变更需可回滚 | 部署单含回滚步骤 | 回滚演练报告 | 半自动 |
| 特权访问需审批 | JIT + 堡垒机 | 授权单 + 会话 | 全自动 |
- id: CC-01 # 生产变更须经授权
check: scripts/check_branch_protection.sh
evidence: artifacts/branch-protection.json
owner: platform
cadence: daily
价值在于同时是可执行清单与审计应答材料:审计问"你怎么保证",答案是一条能跑的检查。
分层不是偷懒,是把控制预算放在后果最重处:
| 层级 | 判据 | 控制强度 | 留痕保留 |
|---|---|---|---|
| 核心 | 资金、个人信息、对外承诺 | 双人审批、JIT、全量留痕 | 长期 |
| 重要 | 影响核心但可降级 | 单人审批 + 门禁 | 中期 |
| 边缘 | 内部工具、可重建 | 门禁即可 | 短期 |
难点是边界会漂移:边缘工具接了真实数据就变成重要系统,控制等级不会自动跟着变。所以分层结论要写进服务元数据,在数据接入评审时复核。
10. 度量
| 指标 | 定义要点 | 健康信号 |
|---|---|---|
| 审计发现数 | 按严重度分组,区分制度缺失与执行偏差 | 下降,偏差占比降低 |
| 证据准备时长 | 取数需求到交付的小时数 | 从"周"到"小时" |
| 控制项自动化率 | 有脚本+证据路径的占比 | 上升 |
| 高危操作可见率 | 被非操作者看到的比例 | 接近 100% |
第二项最能反映成熟度:长期以周计,说明证据是"做出来的",不是"长出来的"。
11. 排错:审计期的五种卡点
| 现象 | 原因 | 处理 |
|---|---|---|
| 找不到某次变更 | 手工热修绕过流水线 | 先禁直连生产,再查历史 |
| 审批只有聊天截图 | 未落在有状态系统 | 迁移入口,IM 仅通知 |
| 无法归因到人 | 共享账号、共享密钥 | 令牌绑流水线身份,人走 SSO |
| 时间戳对不上 | 时钟漂移、日志与工单不同源 | 统一 NTP,事件统一存 UTC |
| 证据链断在构建 | 产物无签名、无摘要留档 | 补 attestation 与归档 |
最后一条最隐蔽:前面有 PR 审批、后面有部署记录,中间那句"这次构建产出的确实是这个镜像"没人能证明,链断在最关键一环。
12. 边界:形式化审批何时收益低于成本
| 场景 | 判断 | 替代做法 |
|---|---|---|
| 纯内部系统、无真实数据 | 正式审批不划算 | 门禁 + 变更通知 |
| 无外部审计要求 | 证据体系可简化 | 留变更与回滚记录 |
| 团队极小、变更极少 | 双人审批变互相盖章 | 事后可见性 + 周复核 |
| 基础设施全托管、无权限面 | 审批对象不存在 | 关注账号与密钥授予留痕 |
| 项目短于审计周期 | 保留期要求不成立 | 只做基本归档 |
判据一句话:**当"证明"的成本高于"做对",且没人真会来看这份证明时,把留痕做薄、把门禁做厚。**留痕边际收益递减很快,门禁收益是实时的。
13. 自测
- "做对了"和"能证明做对了"的差别,会改变哪三个设计决定?
- 三类留痕里,哪一类最容易被共享账号破坏,为什么?
- 判断一级审批是实质还是盖章,看哪三条?
- 为什么事后补录的台账在审计里几乎等于零价值?
- 小团队无法做职责分离时用什么替代,代价是什么?
- 把一条控制项翻译成工程规则,最少要产出哪三样东西?