SOC 2 证据在 GitLab CI 中自动收集:每次合并都会生成一个已签名的证据包,Open Policy Agent 门禁会阻止缺少证据的发布,审计员只需一个数据包链接,而无需截图。
实时审计员仪表盘 · GitLab 项目 · 演示证据 · 路线图
我构建 ShipProof 是因为企业销售在信息安全审查期间屡屡停滞。当审计员要求提供 SOC 2 Type II 变更管理证据时,工程师往往要花两周时间在 Jira、Slack 和 Drive 中搜寻截图。
我希望证据直接存在于部署流水线中。ShipProof 中的每个合并提交都会自动创建一个规范的、已签名的证据包,涵盖测试运行、Semgrep SAST 扫描、Gitleaks 密钥检测、Syft 软件物料清单以及 CODEOWNERS 审批。
我的核心设计原则很严格:LLM 可以总结证据或起草问题描述,但它永远不能批准发布。发布门禁是用 Rego 编写的 Open Policy Agent 评估。如果缺少某个工件,或者某个审批看起来像是走过场,门禁就会失败关闭,停止发布,并提交一个包含修复路径的问题。当门禁允许时,审计员会获得一个指向静态站点或经过身份验证的 API 的单一链接。
我将 ShipProof 映射到 GitLab DevOps 生命周期。它原生自动化了全部九个阶段:
| 阶段 | 我在流水线中自动化的内容 |
|---|---|
| plan | 闭环缺口工作项管理。门禁拒绝会自动打开结构化跟踪问题,并附带复发次数;后续通过的流水线会自动解决并关闭这些问题,并附上修复提交引用。 |
| create | 代码审查和源代码管理。CODEOWNERS 保护策略文件;门禁拒绝五分钟以内的走过场审批;自动化 MR 注释指导审查;Duo 流程叙述 MR 评论。 |
| verify | pytest、Semgrep SAST 和 Gitleaks 在 verify 阶段运行,记录工件哈希。 |
| package | Syft 在 package 阶段生成 CycloneDX SBOM,并将工件哈希写入证据包。 |
| secure | 确定性的 OPA 门禁在发布前检查签名、链哈希和走过场规则。 |
| release | 合规门禁作业阻止部署到生产环境;部署作业需要允许裁决。 |
| configure | Cloud Run 部署清单从环境变量渲染,无需提交密钥。 |
| monitor | 部署后检查验证运行中的容器镜像摘要与证据包批准的提交相匹配。 |
| govern | 不可变的哈希链式账本为审计员数据包、缺口问题和 GitLab Pages 仪表盘提供数据。 |
我使用 Python 3.11 和 Node.js 20 开发了 ShipProof。
python -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt
运行我的测试套件和类型检查:
OPA_BIN=/tmp/opa pytest -q && ruff check src tests demo-app scripts && mypy --strict src scripts
为任意提交收集本地 bundle:
PYTHONPATH=src python -m comply.collect --sha aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --out /tmp/bundle.json
收集器向 stdout 输出一行 JSON,并将完整 bundle 写入磁盘,与 policy/inputs.json 放在一起。如果缺少证明,bundle 会记录为拒绝状态。
PYTHONPATH=src python -m comply.collect --sha <40-hex-sha> --out evidence/bundle.json \
--mr-iid 42 --pipeline-id 1001 --project group/project \
--pipeline-file pipeline.json --approvals-file approvals.json
在 CI 中,scripts/gate_bridge.py 负责准备 pipeline.json,而 scripts/fetch_approvals.py 则从 GitLab API 拉取评审数据。缺失的文件会被记录为缺失状态,而不会被假定为通过。每条 stderr 日志都包含 bundle 关联 ID,敏感信息会被掩码为 ***。
PYTHONPATH=src python -m comply.gate --bundle evidence/bundle.json --ledger logs/ledger.jsonl
退出码 0 表示允许判定。退出码 1 表示拒绝,并附带指明失败控制项的结构化错误。退出码 2 表示参数无效。当门禁在 CI 中阻断时,流水线会触发 python -m comply.gap 以创建跟踪问题。
PYTHONPATH=src python -m comply.packet --ledger logs/ledger.jsonl --release all \
--out packet.json --csv packet.csv --pdf packet.pdf
CSV 导出包含十一列:bundle_id,commit_sha,mr_iid,pipeline_id,control,proof_type,status,artifact_sha,approver_ids,policy_sha,created_at。PDF 通过打印 CSS 从同一 JSON 源渲染。
AUDITOR_TOKEN=local-only PYTHONPATH=src uvicorn comply.api:app --port 8000
使用 bearer 认证查询 bundles:
curl -H "Authorization: Bearer local-only" "localhost:8000/api/v1/bundles?mr_iid=42"
该 API 提供用于 bundle 查询、发布数据包和后台 PDF 导出的端点,受 bearer token 保护,速率限制为每分钟 60 次请求。
cd auditor && npm install && npm run build
界面使用 React 19、TypeScript 和 Tailwind CSS 构建。它通过哈希路由导出为静态站点,可直接在 GitLab Pages 上运行,无需服务器重写。当没有后端 API 时,它使用来自 public/fallback/demo-packets.json 的示例数据。
flowchart LR
A["CI jobs (pytest, Semgrep, Gitleaks, Syft)"] --> B["gate_bridge.py (artifact summary)"]
B --> C["comply.collect (bundle.json + inputs.json)"]
C --> D["comply.sign (Cosign, HMAC fallback)"]
D --> E["ledger.jsonl (hash chain)"]
E --> F["comply.gate (OPA policy + signature + chain)"]
F -->|"deny"| G["comply.gap (gap issue + MR comment)"]
F -->|"allow"| H["deploy jobs (gated)"]
E --> I["comply.packet (JSON, CSV, PDF)"]
I --> J["GitLab Pages dashboard + read API"]
流水线执行单元测试和安全扫描器,将输出桥接到证据收集器,对证据包进行签名,并向账本追加一条记录。门禁作业针对证据包、加密签名和账本链评估 policy/soc2-v1.rego。拒绝时会通知合并请求并创建一个 GitLab 工作项。评估成功则解除部署作业的阻塞并发布审计员数据包。
| 控制项 | 标准 | 证据包中收集的证据 |
|---|---|---|
| CC8.1 变更管理 | 生产变更要求在目标提交上通过代码审查和流水线。大型差异上审查时长不足五分钟即失败。 | 合并请求 IID、审批人 ID、审查时长、差异统计、流水线 ID。 |
| CC7.2 系统监控 | 变更产生不可变、有序的部署日志。 | 流水线作业状态、哈希链式账本、CycloneDX SBOM 摘要、容器标签。 |
| CC6.1 逻辑访问 | 部署必须源自经批准的身份,且不得混用环境。 | 触发身份、密钥检测日志、环境变量名称快照。 |
| CC6.3 基于角色的访问控制 | 未授权角色无法晋升到生产环境。 | 签名者身份、Cosign 证书或签名有效性、OPA 策略评估。 |
| CC7.1 审计关联 | 被阻止的发布必须关联到跟踪问题;已解决的问题必须关联到合并请求。 | 跨评论的问题 IID、合并请求 IID 和证据包 ID 引用。 |
本地执行从 .env 读取。生产流水线使用掩码的 GitLab CI 变量。
| 变量 | 是否必需 | 用途 |
|---|---|---|
GITLAB_TOKEN | 在 CI 中必需 | 收集器和缺口问题生成器使用的项目访问令牌。 |
AUDITOR_TOKEN | 用于 API | comply.api 端点所需的 Bearer 令牌。 |
VANTA_API_KEY | 可选 | 用于证据包同步的 API 密钥;省略时会干净地跳过。 |
GCP_SA_KEY | 仅部署 | 用于 Google Cloud Run 部署的服务账号密钥。 |
GCP_PROJECT_PREVIEW | 仅部署 | 用于预览环境的 Google Cloud 项目 ID。 |
GCP_PROJECT_PROD | 仅部署 | 用于生产部署的 Google Cloud 项目 ID。 |
SHIPPROOF_SIGNING_KEY | 无 Cosign 时 | 当无密钥 Cosign 签名不可用时使用的 HMAC 密钥。 |
OPA_BIN | 可选 | OPA 二进制文件的自定义路径;CI 会自动下载。 |
CI_API_V4_URL | 仅 CI | 获取脚本使用的 GitLab API 根 URL。 |
src/comply/ Collector, gate, signing, ledger, gap reporter, packet builder, read API
policy/ soc2-v1.rego policy contract and evaluation fixtures
tests/ Contract, unit, and API tests
auditor/ Static React auditor interface
demo-app/ FastAPI sample application with container configuration
scripts/ CI bridges and offline seed demonstration scripts
templates/ Markdown templates for gate denial comments
.gitlab/ Duo agent flow definitions and issue templates
demo/ Evidence notes and verified demonstration links
evidence/ logs/ Local bundles and hash-chained ledger storage
我构建并验证了两条演示路径,以确认门禁按预期工作:
demo/good,MR !8):运行测试、SAST、密钥扫描和 SBOM 生成。获得 CODEOWNERS 批准。门禁允许晋升,发布审计员数据包,并在合并请求上发表评论。demo/missing-scan,MR !9):跳过 SAST 扫描器。收集器标记缺失的产物,OPA 以 CC8.1 拒绝发布,流水线停止,comply.gap 提交 issue #3 并附上修复说明。详细的流水线 URL 和评论永久链接记录在 demo/evidence.md 中。
| 子系统 | 当前状态 |
|---|---|
| 收集器、门禁、签名、账本、缺口报告器、数据包构建器、API | 功能完整,并由自动化测试覆盖。 |
| 审计员 Web 应用 | 使用 Vite 可干净构建;部署到 GitLab Pages,并带有离线回退数据。 |
| 流水线门禁和审批检查 | 已在 GitLab CI 中上线;MR 审查检查会查询 GitLab API。 |
| Duo 工作流 | 定义随 .gitlab/duo/flows/ 提供,不包含部署审批权限。 |
| 演示路径 | 已通过离线脚本验证,并记录在 demo/evidence.md 中。 |
| 多框架支持 | 已在 ROADMAP.md 中设计,用于新增 ISO 27001 和 HIPAA 支持。 |
我将 policy/ 视为严格由人工控制。自动化代理不得修改此目录中的文件。如需提出策略变更,请打开一个仅修改 policy/soc2-v1.rego 的合并请求,运行测试套件,并更新文档中固定的 SHA256 摘要。当前固定的策略摘要为 0f9c8199eb9216c45c5b2a982550363d6214d6fceefcd052f25f6839e476484a。
MIT(版权所有 2026 Divyanshu)。