Cosign签名验证自动化测试:从单元测试到E2E测试的完整策略
【免费下载链接】cosign Container Signing 项目地址: https://gitcode.com/GitHub_Trending/co/cosign
在容器化应用部署流程中,签名验证是保障供应链安全的关键环节。Cosign作为容器签名工具,其自身的测试策略直接影响签名验证功能的可靠性。本文将系统介绍Cosign项目的多层级测试架构,包括单元测试、集成测试和端到端(E2E)测试的实现方式,并通过具体代码示例和测试数据展示如何构建完整的测试闭环。
测试架构概览
Cosign的测试体系采用金字塔模型设计,从底层的单元测试到顶层的E2E测试形成完整覆盖。项目测试代码主要集中在test/目录,包含以下核心模块:
- 单元测试:验证独立函数和方法的逻辑正确性,如密钥生成、签名算法实现等
- 集成测试:测试不同模块间的协作,如签名生成与验证流程的衔接
- E2E测试:模拟真实环境中的完整操作流程,验证端到端功能正确性
测试目录结构
项目测试相关文件组织如下:
test/
├── e2e_test.go # 核心端到端测试用例
├── e2e_test.sh # E2E测试执行脚本
├── helpers.go # 测试辅助函数库
├── testdata/ # 测试数据文件
│ ├── attestations/ # 验证用的证明文件
│ ├── policies/ # 策略验证规则
│ └── *.json # 各类测试 payload
└── ci.mk # CI环境测试配置
单元测试:核心功能组件验证
单元测试聚焦于独立功能点的验证,确保每个组件的逻辑正确性。在Cosign中,签名生成与验证的核心逻辑在pkg/cosign/目录实现,对应的单元测试覆盖了密钥管理、签名算法、验证逻辑等关键功能。
密钥对生成测试
密钥对生成是签名功能的基础,test/helpers.go中的keypair函数提供了测试环境下的密钥生成能力:
// 生成测试用密钥对
func keypair(t *testing.T, td string) (*cosign.KeysBytes, string, string) {
return keypairWithAlgorithm(t, td, v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256)
}
该函数封装了Cosign的密钥生成逻辑,支持指定加密算法,返回公钥和私钥的文件路径,便于在测试中直接使用。测试用例通过调用此函数创建临时密钥对,验证密钥生成、存储和加载的正确性。
签名验证逻辑测试
签名验证的核心逻辑在cliverify.VerifyCommand中实现,单元测试通过模拟不同输入参数验证验证逻辑的鲁棒性。例如,test/helpers.go中定义的verify函数封装了验证命令的调用:
// 验证函数封装
var verify = func(keyRef, imageRef string, checkClaims bool, annotations map[string]interface{}, attachment string, skipTlogVerify bool) error {
cmd := cliverify.VerifyCommand{
KeyRef: keyRef,
RekorURL: rekorURL,
CheckClaims: checkClaims,
Annotations: sigs.AnnotationsMap{Annotations: annotations},
Attachment: attachment,
HashAlgorithm: crypto.SHA256,
MaxWorkers: 10,
IgnoreTlog: skipTlogVerify,
}
return cmd.Exec(context.Background(), []string{imageRef})
}
此函数允许测试用例灵活设置验证参数,验证不同场景下的签名验证行为,如带注释的签名验证、跳过日志验证等特殊情况。
集成测试:模块协作验证
集成测试关注不同组件间的交互逻辑,验证模块协作的正确性。在Cosign中,签名-验证流程涉及密钥管理、OIDC认证、TUF元数据等多个模块,集成测试确保这些模块能正确协同工作。
签名验证流程测试
test/e2e_test.go中的TestSignVerify函数实现了完整的签名-验证流程测试:
func TestSignVerify(t *testing.T) {
td := t.TempDir()
// 设置测试环境变量
err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey",
env.VariableSigstoreRekorPublicKey.String(), td)
must(err, t)
// 启动本地 registry
repo, stop := reg(t)
defer stop()
imgName := path.Join(repo, "cosign-e2e")
_, _, cleanup := mkimage(t, imgName)
defer cleanup()
// 生成密钥对
_, privKeyPath, pubKeyPath := keypair(t, td)
ctx := context.Background()
// 初始验证应失败(未签名)
mustErr(verify(pubKeyPath, imgName, true, nil, "", false), t)
// 执行签名
ko := options.KeyOpts{
KeyRef: privKeyPath,
PassFunc: passFunc,
RekorURL: rekorURL,
SkipConfirmation: true,
}
so := options.SignOptions{Upload: true, TlogUpload: true}
must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
// 验证签名成功
must(verify(pubKeyPath, imgName, true, nil, "", false), t)
}
该测试用例完整模拟了"生成密钥→签名镜像→验证签名"的流程,验证了各环节的协作正确性。测试中使用了本地临时registry(test/helpers.go中的reg函数)和测试镜像,确保环境隔离性。
E2E测试:真实场景模拟验证
E2E测试是验证Cosign在真实环境中工作能力的关键环节。Cosign的E2E测试通过test/e2e_test.go实现,模拟了用户的实际操作流程,包括与Fulcio、Rekor等服务的集成。
完整测试流程设计
E2E测试模拟真实用户场景,包含以下关键步骤:
- 启动本地测试环境(Registry、Fulcio、Rekor服务)
- 生成测试用密钥对或获取OIDC身份令牌
- 执行镜像签名操作
- 验证签名有效性
- 清理测试环境
以下是带证书链验证的E2E测试示例:
// 证书链验证测试
func TestSignVerifyCertBundle(t *testing.T) {
td := t.TempDir()
// 配置测试环境变量
err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey",
env.VariableSigstoreRekorPublicKey.String(), td)
must(err, t)
// 启动本地 registry
repo, stop := reg(t)
defer stop()
imgName := path.Join(repo, "cosign-e2e")
_, _, cleanup := mkimage(t, imgName)
defer cleanup()
// 生成测试证书链
_, privKeyPath, pubKeyPath := keypair(t, td)
caCertFile, _, caIntermediateCertFile, _, certFile, certChainFile, err :=
generateCertificateBundleFiles(td, true, "foobar")
must(err, t)
// 初始验证应失败
mustErr(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile,
imgName, true, nil, "", true), t)
// 执行签名
ko := options.KeyOpts{
KeyRef: privKeyPath,
PassFunc: passFunc,
RekorURL: rekorURL,
SkipConfirmation: true,
}
so := options.SignOptions{Upload: true, TlogUpload: true}
must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
// 验证证书链
ignoreTlog := true
must(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile,
imgName, true, nil, "", ignoreTlog), t)
// 验证证书链文件
must(verifyCertChain(pubKeyPath, certChainFile, certFile,
imgName, true, nil, "", ignoreTlog), t)
}
测试数据管理
E2E测试依赖多种测试数据,如策略文件、证明文件和密钥材料,这些文件集中存储在test/testdata/目录:
-
策略文件:如test/testdata/policies/cue-works.cue定义了验证规则:
import "time" before: time.Parse(time.RFC3339, "2049-10-09T17:10:27Z") // 验证 predicateType 字段 predicateType: "https://cosign.sigstore.dev/attestation/v1" // 验证时间戳约束 predicate: { Timestamp: <before } -
证明文件:如test/testdata/attestations/vuln-predicate.json提供了漏洞扫描证明示例:
{ "invocation": { "parameters": null, "uri": "invocation.example.com/cosign-testing" }, "scanner": { "uri": "fakescanner.example.com/cosign-testing" }, "metadata": { "scanStartedOn": "2022-04-12T00:00:00Z", "scanFinishedOn": "2022-04-12T00:10:00Z" } }
测试环境与CI集成
Cosign的测试流程与CI/CD pipeline深度集成,通过test/ci.mk定义了自动化测试流程,确保每次代码提交都经过全面测试验证。
测试执行脚本
test/e2e_test.sh提供了E2E测试的执行入口,负责启动依赖服务、执行测试用例和生成报告:
#!/bin/bash
set -euo pipefail
# 启动本地 Rekor 服务
rekor-server serve --trillian_log_server.address=localhost:8090 &
# 等待服务就绪
until curl -f http://localhost:3000/health; do
echo "等待 Rekor 服务启动..."
sleep 1
done
# 执行 E2E 测试
go test -count=1 -timeout=30m -run=^TestSignVerify$ ./test/...
测试覆盖率报告
Cosign项目通过CI流程生成测试覆盖率报告,确保核心功能的测试覆盖。开发者可通过以下命令本地生成覆盖率报告:
# 运行所有测试并生成覆盖率报告
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
# 查看覆盖率详情
go tool cover -html=coverage.txt
高级测试场景
Cosign的测试策略还覆盖了多种复杂场景,确保工具在各种边缘情况下的稳定性。
密钥轮换与证书链验证
test/e2e_test.go中的TestSignVerifyCertBundle测试验证了证书链验证功能,支持多层级CA结构的签名验证:
// 生成测试证书链
caCertFile, _, caIntermediateCertFile, _, certFile, certChainFile, err :=
generateCertificateBundleFiles(td, true, "foobar")
must(err, t)
// 使用证书链验证
must(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile,
imgName, true, nil, "", ignoreTlog), t)
TUF元数据验证
test/e2e_test.go中的TestSignVerifyWithTUFMirror测试验证了基于TUF(The Update Framework)的元数据验证能力,确保签名验证过程中使用的根证书和密钥材料的安全性。
测试最佳实践总结
基于Cosign项目的测试实现,我们可以总结出容器签名工具的测试最佳实践:
- 环境隔离:使用临时目录和本地服务实例,避免测试相互干扰
- 数据驱动:使用多样化的测试数据覆盖不同场景
- 流程完整:从签名生成到验证的全流程测试
- 安全默认:测试用例包含安全策略验证,如过期检查、权限控制
- 自动化集成:与CI/CD流程深度集成,确保每次提交都经过验证
通过本文介绍的测试策略,Cosign项目构建了可靠的质量保障体系,确保签名验证功能的正确性和安全性。开发者在使用Cosign时,也可参考类似的测试方法验证自身的签名验证流程。
扩展阅读
- 官方测试文档:test/README.md
- 签名规范:specs/SIGNATURE_SPEC.md
- E2E测试配置:test/ci.mk
【免费下载链接】cosign Container Signing 项目地址: https://gitcode.com/GitHub_Trending/co/cosign
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



