MemerGamer/devsecops-attestation
GitHub: MemerGamer/devsecops-attestation
一个基于零信任设计的 DevSecOps 证明工具,通过 Ed25519 签名将 CI/CD 流水线中的安全检查结果构建为防篡改的加密证明链,并结合 OPA 策略实现可信的自动化部署门控。
Stars: 2 | Forks: 0
# DevSecOps 证明


[](https://github.com/MemerGamer/devsecops-attestation/actions/workflows/devsecops-pipeline.yml)
[](https://codecov.io/gh/MemerGamer/devsecops-attestation)
CI/CD pipeline 中可加密验证的安全决策。
**硕士论文:** 基于 CI/CD 的 DevSecOps Pipeline 中可加密验证的安全决策
**作者:** Kovács Bálint-Hunor — Sapientia EMTE, Marosvásárhelyi Kar
## 目录
- [概述](#overview)
- [零信任设计](#zero-trust-design)
- [前置条件](#prerequisites)
- [快速开始](#quick-start)
- [GitHub Actions 设置](#github-actions-setup)
- [运行测试](#running-tests)
- [文档](#documentation)
- [License](#license)
## 概述
CI/CD pipeline 中的每一项安全检查(SAST、SCA、config scan、secret scan)
都会生成一个 Ed25519 签名的 JSON 证明。各项证明会链接成一条链:
每个证明都包含前一个证明的 SHA-256 摘要,使得插入、删除
或重新排序等操作可以被检测到。部署门控(deployment gate)会加载该链,验证每一个
签名以及链的链接关系,然后根据 OPA/Rego
策略对结果进行评估,从而生成 ALLOW 或 BLOCK 决策。
## 零信任设计
系统在整个证明的生命周期中贯彻零信任原则:
| 控制 | 机制 |
|---------|-----------|
| 每种检查类型对应的签名密钥 | 每种检查类型(sast、sca、config、secret)使用专用的 Ed25519 密钥对。泄露的 SAST 密钥无法伪造 SCA 证明。 |
| 加密绑定的签名者身份 | `SignerID`(例如 `github-runner:Linux`)包含在规范化的 payload 中,并受 Ed25519 签名保护。签名后注入的操作是可检测的。 |
| 时间戳强制校验 | `VerifyChainWithOptions` 拒绝未来的时间戳(容忍 60 秒时钟偏差)、时间戳倒退以及超过 `--max-age` 的证明。 |
| 策略文件完整性 | `--policy-hash` 锁定 Rego 策略文件的 SHA-256。修改后的策略文件在评估前会被拒绝。 |
| 透明度日志引用 | 每个证明都包含一个 `log_entry` URL(即 GitHub Actions 运行记录)。`--require-log-entries` 使得这在门控处成为强制要求。 |
| 显式的签名者授权 | 门控要求提供 `--verify-signer`(单一共享密钥)或 `--authorized-signers`(基于检查类型的映射)。两者缺一不可。在策略运行前,Go 会强制执行授权。 |
| 链的预验证 | 策略永远不会在未验证的链上评估。断裂的链会导致门控退出并返回状态码 1,而不会去查询 OPA。 |
| 无重复检查类型 | `VerifyChain` 拒绝同一检查类型出现多次的链,防止单个步骤的重放攻击。 |
## 前置条件
- Go 1.26 或更高版本
## 快速开始
### 生成密钥对
每种检查类型生成一对密钥。每对密钥都是独立的,因此密钥
泄露仅限单个检查类型。
```
mkdir -p keys
for check in sast sca config secret; do
go run ./cmd/keygen --out "keys/$check"
done
# 每个目录包含 private.hex(保密)和 public.hex
```
### 签署安全结果
每个结果文件必须是符合以下格式的 JSON 对象:
```
{ "passed": true, "findings": [] }
```
发现(Findings,可选)的格式如下:
```
{ "id": "CWE-89", "severity": "critical", "title": "SQL injection", "location": "src/db.go:42" }
```
使用各自的密钥签署全部四项检查:
```
REF=$(git rev-parse HEAD)
LOG_URL="https://github.com/org/repo/actions/runs/12345"
go run ./cmd/sign \
--check-type sast --tool semgrep \
--result results/sast.json \
--target-ref "$REF" --subject myapp \
--signing-key "$(cat keys/sast/private.hex)" \
--signer-id "local:$(whoami)" \
--log-entry "$LOG_URL" \
--chain chain.json
go run ./cmd/sign \
--check-type sca --tool trivy \
--result results/sca.json \
--target-ref "$REF" --subject myapp \
--signing-key "$(cat keys/sca/private.hex)" \
--signer-id "local:$(whoami)" \
--log-entry "$LOG_URL" \
--chain chain.json
go run ./cmd/sign \
--check-type config --tool checkov \
--result results/config.json \
--target-ref "$REF" --subject myapp \
--signing-key "$(cat keys/config/private.hex)" \
--signer-id "local:$(whoami)" \
--log-entry "$LOG_URL" \
--chain chain.json
go run ./cmd/sign \
--check-type secret --tool gitleaks \
--result results/secret.json \
--target-ref "$REF" --subject myapp \
--signing-key "$(cat keys/secret/private.hex)" \
--signer-id "local:$(whoami)" \
--log-entry "$LOG_URL" \
--chain chain.json
```
### 验证链完整性
```
go run ./cmd/verify --chain chain.json
```
这会验证所有的 Ed25519 签名、链的链接关系、主体一致性
和时间戳顺序。传入 `--verify-signer ` 还可以检查
每个证明是否由特定的密钥签署。
### 评估部署门控
```
SAST_PUB=$(cat keys/sast/public.hex)
SCA_PUB=$(cat keys/sca/public.hex)
CONFIG_PUB=$(cat keys/config/public.hex)
SECRET_PUB=$(cat keys/secret/public.hex)
go run ./cmd/gate evaluate \
--chain chain.json \
--authorized-signers "sast=$SAST_PUB,sca=$SCA_PUB,config=$CONFIG_PUB,secret=$SECRET_PUB" \
--policy .github/policies/deploy.rego \
--policy-hash "$(sha256sum .github/policies/deploy.rego | cut -d' ' -f1)" \
--max-age 24h \
--require-log-entries
```
退出码 0 表示门控允许部署。退出码 1 表示已被阻止
(链无效、策略拒绝或零信任检查失败)。`--output`
标志会输出完整的决策 JSON。
**备选方案:单一共享密钥**(更简单,隔离性较差)
```
go run ./cmd/gate evaluate \
--chain chain.json \
--verify-signer "$(cat keys/shared/public.hex)"
```
## GitHub Actions 设置
该 pipeline 使用每种检查类型对应的密钥对。每种检查类型都有自己
专用的签名密钥,因此泄露仅限于单次检查。
**1. 在本地生成四对密钥:**
```
for check in sast sca config secret; do
go run ./cmd/keygen --out "keys/$check"
done
```
**2. 将全部八个 secret 添加到你的仓库中:**
前往:**Settings > Secrets and variables > Actions > New repository secret**
| Secret 名称 | 值 |
|---|---|
| `SAST_SIGNING_KEY` | `keys/sast/private.hex` 的内容 |
| `SCA_SIGNING_KEY` | `keys/sca/private.hex` 的内容 |
| `CONFIG_SIGNING_KEY` | `keys/config/private.hex` 的内容 |
| `SECRET_SCANNING_SIGNING_KEY` | `keys/secret/private.hex` 的内容 |
| `SAST_PUBLIC_KEY` | `keys/sast/public.hex` 的内容 |
| `SCA_PUBLIC_KEY` | `keys/sca/public.hex` 的内容 |
| `CONFIG_PUBLIC_KEY` | `keys/config/public.hex` 的内容 |
| `SECRET_SCANNING_PUBLIC_KEY` | `keys/secret/public.hex` 的内容 |
切勿提交任何 `private.hex` 文件。`keys/` 目录已包含在 `.gitignore` 中。
**3. 策略哈希(保持同步):**
门控步骤通过 `--policy-hash` 锁定 `deploy.rego` 的 SHA-256。如果你
更新了策略,请重新计算哈希并更新 workflow:
```
sha256sum .github/policies/deploy.rego
```
然后更新 `.github/workflows/devsecops-pipeline.yml` 中的 `--policy-hash`。
**4. 生产环境(可选):**
`deploy-gate` 作业针对 `production` 环境,该环境可以
配置为在部署前需要人工审批。在
**Settings > Environments > production > Required reviewers** 下进行设置。
## 运行测试
### 单元测试
```
go test ./...
```
### 开启竞态检测器
```
go test -race ./...
```
### 集成测试
```
go test -tags integration ./test/integration/...
```
集成测试会构建 CLI 二进制文件并运行端到端的 pipeline 场景,
包括篡改检测攻击模拟。
## 文档
- [架构](docs/architecture.md) - 系统设计、数据流和加密保证
- [架构图](docs/devsecops_attestation_architecture.svg) - 可视化概述
- [项目结构](docs/structure.md) - package 布局、职责及关键设计决策
- [实施计划](docs/implementation-plan.md) - 开发阶段和当前状态
- [博士扩展路径](docs/phd-extension.md) - 超出硕士范围的计划研究扩展
- [相关工作](docs/related-work.md) - 现有技术和相关标准
## License
MIT - 查看 [LICENSE](LICENSE)
标签:DevSecOps, Ed25519, EVTX分析, Go, OPA/Rego, Ruby工具, 上游代理, 密码学验证, 日志审计, 结构化提示词