cosign verify-attestation 完全指南:容器镜像 in-toto 证明(Attestation)校验实战
【免费下载链接】cosignCode signing and transparency for containers and binaries项目地址: https://gitcode.com/GitHub_Trending/co/cosign
导读
cosign verify-attestation是 sigstore 生态工具 cosign 提供的用于校验容器镜像上 in-toto 格式证明(Attestation)的核心命令。本文以 doc/cosign_verify-attestation.md 为骨架,结合本仓库源码深入讲解其工作流程、全部命令行参数、多种密钥来源与策略校验用法,帮助读者掌握从"验证证明签名"到"用 CUE/Rego 策略约束证明内容"的完整技能。
一、命令概述:校验镜像上的 Attestation
cosign verify-attestation用于校验已附加到容器镜像上的 in-toto attestation(证明)。与cosign verify校验普通签名不同,attestation 校验的核心对象是遵循 in-toto 规范的 Statement 结构——它由 DSSE 信封包装,内含 subject(镜像摘要)与 predicate(如 SLSA provenance、SPDX SBOM、漏洞扫描结果等)。
官方 Synopsis 对它的定位是:
Verify an attestation on an image by checking the claims against the transparency log.
即:校验镜像上的 attestation,并将其中的 claims 与透明日志(Rekor)进行比对,确保证明内容可公开验证、可追溯。
命令基础语法:
cosign verify-attestation [flags]完整命令声明位于 cmd/cosign/cli/verify.go,其核心执行逻辑由 cmd/cosign/cli/verify/verify_attestation.go 中的VerifyAttestationCommand.Exec承担。
1.1 与cosign attest的配对关系
verify-attestation通常验证的是cosign attest命令(实现见 cmd/cosign/cli/attest/attest.go)生成的证明。attest会构建 in-toto Statement、签名后将 attestation 以 OCI artifact 形式写回镜像仓库;verify-attestation则从仓库拉取这些 attestation,先做签名/证书/透明日志校验,再按 predicate type 过滤并输出。
二、完整命令示例(继承原文档并逐条解读)
原文档给出的示例覆盖了密钥验证、多镜像、注解、本地镜像、KMS/URL 密钥、策略校验等全部典型场景:
cosign verify-attestation --key <key path>|<key url>|<kms uri> <image uri> [<image uri> ...]2.1 基础用法
# 对镜像上的 cosign attestation 进行验证(校验其与透明日志的一致性) cosign verify-attestation <IMAGE> # 一次校验多个镜像 cosign verify-attestation <IMAGE_1> <IMAGE_2> ... # 额外校验指定的注解 cosign verify-attestation -a key1=val1 -a key2=val2 <IMAGE>说明:
- 第一个示例不带
--key,走的是keyless 流程(依赖 Fulcio 证书 + 透明日志)。 - 多镜像场景在源码中体现为
Exec中的for _, imageRef := range images循环(verify_attestation.go),每个镜像独立完成抓取与校验。 -a/--annotation来自AnnotationOptions,会注入CheckOpts.Annotations,校验时要求证明携带匹配的注解。
2.2 使用公钥验证
# 使用本地公钥文件验证镜像 cosign verify-attestation --key cosign.pub <IMAGE> # 验证来自 'cosign save' 的本地磁盘镜像 cosign verify-attestation --key cosign.pub --local-image <PATH> # 使用 URL 提供的公钥验证 cosign verify-attestation --key https://host.for/<FILE> <IMAGE>--local-image对应源码中的VerifyLocalImageAttestations分支(verify_attestation.go),用于验证cosign save保存到本地的镜像布局;同时会自动检测本地镜像中的 bundle 格式(HasLocalAttestationBundles)。
2.3 使用 KMS / 密钥托管服务中的公钥验证
# Google Cloud KMS cosign verify-attestation --key gcpkms://projects/<PROJECT>/locations/global/keyRings/<KEYRING>/cryptoKeys/<KEY> <IMAGE> # Hashicorp Vault cosign verify-attestation --key hashivault:///<KEY> <IMAGE> # GitLab(按项目名) cosign verify-attestation --key gitlab://[OWNER]/[PROJECT_NAME] <IMAGE> # GitLab(按项目 ID) cosign verify-attestation --key gitlab://[PROJECT_ID] <IMAGE>--key参数支持三类来源:本地文件路径、KMS URI、Kubernetes Secret(见 options/verify.go)。KMS URI 通过LoadVerifierFromKeyOrCert→PublicKeyFromKeyRefWithHashAlgo加载(verify/common.go)。
2.4 基于策略的证明内容校验
# 使用 Rego 策略校验 attestation cosign verify-attestation --key cosign.pub --type <PREDICATE_TYPE> --policy <REGO_POLICY> <IMAGE> # 使用 CUE 策略校验 attestation cosign verify-attestation --key cosign.pub --type <PREDICATE_TYPE> --policy <CUE_POLICY> <IMAGE>这是verify-attestation区别于普通verify的杀手锏:签名校验只回答"证明是谁签的",策略校验回答"证明内容是否满足约束"。
三、Attestation 校验的底层工作流程
从 verify_attestation.go 的Exec源码可以还原完整校验流水线:
- 参数互斥检查:
--key与--certificate-identity、--key与--sk不可同时出现(options.NOf(...) > 1时报KeyAndIdentityParseError/KeyParseError)。 - 构造客户端:
ClientOpts构建 OCI registry 客户端选项;--allow-http-registry/--allow-insecure-registry时附加name.Insecure。 - 组装
CheckOpts:注入身份(Identities)、GitHub Workflow 断言、SCT/透明日志开关、MaxWorkers并发数等。 - 本地/远端镜像分流:
--local-image走cosign.VerifyLocalImageAttestations,否则走cosign.VerifyImageAttestations(内部包含 DSSE 签名验证、Fulcio 证书链验证、透明日志包含性证明校验)。 - 策略文件分类:根据扩展名把
--policy文件分为.rego与.cue两类,其他扩展名直接报invalid policy format。 - predicate 类型匹配:通过
policy.AttestationToPayloadJSON(pkg/policy/attestation.go)解析 DSSE 信封,解码 base64 的 payload,反序列化 in-toto Statement,只有当statement.PredicateType == 期望的 predicateURI时才进入策略校验;不匹配的证明会被跳过并记录实际类型,最终给出 "none of the attestations matched the predicate type" 的提示,便于用户发现--type拼写错误。 - CUE/Rego 策略执行:
cue.ValidateJSON、rego.ValidateJSON对 payload 进行校验,任一失败都会累计validationErrors并在最后汇总输出。 - 结果输出:
PrintVerificationHeader打印校验摘要(claims 校验、透明日志存在性、公钥校验、证书校验等),再以 text 或 json 格式打印证明内容。
3.1 predicate 类型映射表
--type的取值由 options/predicate.go 中的PredicateTypeMap定义:
--type取值 | 对应的 predicate URI |
|---|---|
custom(默认) | https://cosign.sigstore.dev/attestation/v1 |
slsaprovenance/slsaprovenance02 | SLSA Provenance v0.2 |
slsaprovenance1 | SLSA Provenance v1 |
spdx/spdxjson | in-toto SPDX |
cyclonedx | in-toto CycloneDX |
link | in-toto Link v1 |
vuln | https://cosign.sigstore.dev/attestation/vuln/v1 |
openvex | OpenVEX 命名空间 |
| 任意合法 URI | 直接作为 predicate URI 使用(url.ParseRequestURI校验) |
注意:--type默认值为custom,如果镜像上的证明是 SLSA 等类型而--type未指定,会因为 predicate 不匹配而被跳过并提示实际找到的类型。
四、全部选项(Flags)详解
4.1 证书与身份校验类
| 选项 | 说明 |
|---|---|
--certificate-github-workflow-name string | GitHub OIDC token 的workflowclaim,即工作流名称 |
--certificate-github-workflow-ref string | GitHub OIDC token 的refclaim,即工作流运行所基于的 git ref |
--certificate-github-workflow-repository string | GitHub OIDC token 的repositoryclaim,即工作流所属仓库 |
--certificate-github-workflow-sha string | GitHub OIDC token 的shaclaim,即工作流运行所基于的 commit SHA |
--certificate-github-workflow-trigger string | GitHub OIDC token 的event_nameclaim,即触发工作流运行的事件名 |
--certificate-identity string | Fulcio 证书中期望的身份,可取 email、DNS 名、IP、URI;keyless 流程中必须与--certificate-identity-regexp二选一 |
--certificate-identity-regexp string | --certificate-identity的正则替代版(Go RE2 语法) |
--certificate-oidc-issuer string | 期望的 OIDC issuer,如https://token.actions.githubusercontent.com或https://oauth2.sigstore.dev/auth |
--certificate-oidc-issuer-regexp string | OIDC issuer 的正则替代版 |
--insecure-ignore-sct | 不校验证书是否内嵌 SCT(证书透明日志包含性证明) |
这些选项由CertVerifyOptions(options/certificate.go)注册。keyless 流程强制要求身份断言:Identities()方法在--certificate-identity/--certificate-identity-regexp与--certificate-oidc-issuer/--certificate-oidc-issuer-regexp均未提供时报错。测试 verify_attestation_test.go 分别验证了"缺 identity 报错"和"缺 issuer 报错"两个场景。
4.2 签名材料与透明日志类
| 选项 | 说明 |
|---|---|
--allow-certificate-chain | 允许在 v0.3+ bundle 的校验材料中使用 X.509 证书链(对应sgbundle.AllowCertificateChain()) |
--check-claims | 是否校验 claims,默认true(为 true 时注入cosign.IntotoSubjectClaimVerifier) |
--insecure-ignore-tlog | 忽略透明日志校验,仅当证明未上传透明日志时使用;未入日志的工件无法被公开验证 |
--key string | 公钥文件路径、KMS URI 或 Kubernetes Secret |
--local-image | 指定镜像为cosign save保存的本地路径 |
--trusted-root string | Sigstore TrustedRoot JSON 文件路径(root.NewTrustedRootFromPath加载) |
--use-signed-timestamps | 校验 RFC3161 时间戳(与TSACertChainPath任一非空即开启) |
当使用--new-bundle-format(默认开启)时,CheckSigstoreBundleUnsupportedOptions(verify/common.go)会拒绝通过旧参数提供证书链、SCT、TSA 链等材料——这些必须统一走 bundle +--trusted-root。
4.3 策略与输出类
| 选项 | 说明 |
|---|---|
--policy strings | 指定用于校验的 CUE(.cue)或 Rego(.rego)策略文件,可重复传入 |
--type string | 指定 predicate 类型:slsaprovenance、slsaprovenance02、slsaprovenance1、link、spdx、spdxjson、cyclonedx、vuln、openvex、custom或任意 URI,默认custom |
-o, --output string | 输出格式,json(默认)或text |
4.4 安全密钥与 Registry 连接类
| 选项 | 说明 |
|---|---|
--sk | 使用硬件安全密钥(PIV,对应pivkey.GetKeyWithSlot) |
--slot string | 安全密钥槽位:authentication、signature(默认)、card-authentication、key-management |
--k8s-keychain | 使用 Kubernetes keychain 替代默认 keychain(支持 workload identity) |
--max-workers int | 并行执行的最大 worker 数,默认10(cosign.DefaultMaxWorkers) |
--allow-http-registry | 允许通过 HTTP 连接 registry(仅限测试) |
--allow-insecure-registry | 允许不安全连接 registry(如过期或自签名 TLS 证书,仅限测试) |
--registry-cacert string | 连接 registry 用的 X.509 CA 证书(PEM)路径 |
--registry-client-cert string | mTLS 客户端证书(PEM)路径 |
--registry-client-key string | 与--registry-client-cert配套的私钥(PEM)路径 |
--registry-password string | registry 基本认证密码 |
--registry-server-name string | mTLS 连接中tls.Config的ServerName(SAN 名) |
--registry-token string | registry bearer token |
--registry-username string | registry 基本认证用户名 |
4.5 继承自父命令的选项
--output-file string # 将日志输出到文件 -t, --timeout duration # 命令超时时间,默认 3m0s -d, --verbose # 输出调试日志五、实战一:Keyless 验证 GitHub Actions 生成的证明
在 CI 场景中,证明通常由 GitHub Actions 的 OIDC 身份签名(Fulcio 证书内嵌 workflow 信息),验证方无需公钥即可完成信任建立:
cosign verify-attestation \ --certificate-identity "https://github.com/<ORG>/<REPO>/.github/workflows/build.yml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ --type slsaprovenance1 \ ghcr.io/<ORG>/<IMAGE>@sha256:...认证通过后,PrintVerification(verify/common.go)会解析证书扩展,输出Certificate subject、Certificate issuer URL、GitHub Workflow Trigger/SHA/Name/Repository/Ref等溯源信息。
六、实战二:用 CUE / Rego 策略约束证明内容
6.1 CUE 策略示例
仓库测试用例 test/testdata/policies/cue-works.cue 提供了一个可直接参考的 CUE 策略:
import "time" before: time.Parse(time.RFC3339, "2049-10-09T17:10:27Z") // The predicateType field must match this string predicateType: "https://cosign.sigstore.dev/attestation/v1" // The predicate must match the following constraints. predicate: { Timestamp: <before }使用方式:
cosign verify-attestation \ --key cosign.pub \ --type custom \ --policy cue-works.cue \ <IMAGE>该策略断言:predicateType必须精确等于https://cosign.sigstore.dev/attestation/v1,且 predicate 中的Timestamp必须早于 2049 年(time.Parse为 CUE 内置函数)。
6.2 Rego 策略示例
Rego 策略文件同样通过--policy传入,cosign 会对策略引擎可消费的 JSON(经AttestationToPayloadJSON转换后的 Statement JSON)执行rego.ValidateJSON。仓库另有对应的失败用例 test/testdata/policies/cue-fails.cue、test/testdata/policies/cue-vuln-fails.cue 等,可用于对照验证策略的"通过/拦截"分支。
6.3 策略校验的源码行为细节
- 扩展名必须是
.rego或.cue,否则直接报错invalid policy format, expected .cue or .rego。 - 校验失败会打印错误总数与逐条错误,并以非零退出码返回
%d validation errors occurred。 - 若所有 attestation 的 predicate 类型都不匹配
--type,返回none of the attestations matched the predicate type: <type>, found: <实际类型列表>——这一设计(attestation.go 返回实际 predicateType 供报错提示)能有效帮助用户排查--type拼写或类型选择错误。
七、验证测试与常见错误场景
仓库在 verify_attestation_test.go 中固化了以下校验规则:
| 测试场景 | 期望结果 |
|---|---|
只给--certificate不给 identity | 报need --certificate-identity |
| 只给 identity 不给 issuer | 报need --certificate-oidc-issuer |
--key与--certificate-identity同时给出 | KeyAndIdentityParseError(互斥) |
--key与--certificate-identity-regexp同时给出 | KeyAndIdentityParseError(互斥) |
| identity 与 identity-regexp 同时给出 | KeyAndIdentityParseError(互斥) |
--key与--sk同时给出 | KeyParseError(互斥) |
仅--sk且无可用 PIV token | 报opening piv token错误 |
此外,pkg/policy/attestation_test.go 还覆盖了 payload 解析的各类异常:非法 JSON、payload 非字符串、缺少 payload 字段、base64 解码失败、Payload()读取失败等,均会返回带明确上下文的错误信息。
八、与关联命令的协同
- 生成证明用
cosign attest(attest.go),验证用cosign verify-attestation; - 验证普通镜像签名(非 attestation)用
cosign verify,其文档见 doc/cosign_verify.md; - 验证二进制/文件 blob 的 attestation 用
cosign verify-blob-attestation; - 从镜像下载 attestation 内容可用
cosign download attestation; --trusted-root、bundle 的打包与升级参见 doc/cosign_bundle.md 与 doc/cosign_trusted-root.md;- 父命令总览见 doc/cosign.md。
九、总结
cosign verify-attestation是 sigstore 供应链安全体系中连接"证明生成"与"策略执行"的关键一环。它既支持传统公钥验证、也支持 KMS/URL/本地多来源密钥,更通过--type+--policy组合将验证从"签名可信"提升到"内容合规"。理解其参数语义、predicate 类型映射与 CUE/Rego 策略机制,是在生产环境中落地可审计、可追溯的软件供应链验证的基础能力。
【免费下载链接】cosignCode signing and transparency for containers and binaries项目地址: https://gitcode.com/GitHub_Trending/co/cosign
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考