cosign verify 完全指南:容器镜像签名验证的原理、参数与实战
【免费下载链接】cosignCode signing and transparency for containers and binaries项目地址: https://gitcode.com/GitHub_Trending/co/cosign
本篇技术指南围绕 sigstore cosign 项目的cosign verify命令展开,系统讲解其命令语法、全部命令行参数、多场景验证示例(公开密钥、KMS、keyless 证书链、本地镜像等),并结合仓库源码(cmd/cosign/cli/verify/verify.go、pkg/cosign/verify.go)深入剖析透明日志(Transparency Log)、claims 校验、证书链验证等底层机制。读完本文,你将能够独立完成对任意已签名容器镜像的签名真实性、归属身份与注解信息的完整校验,并理解验证失败时的排查方向。
一、命令定位:cosign verify 做什么
cosign verify用于验证指定容器镜像(container image)上的签名,通过将签名中的 claims 与透明度日志(transparency log)进行核对,从而确认镜像的签名真实有效、未被篡改。命令入口定义在 cmd/cosign/cli/verify.go,其职责声明为:
Verify a signature on the supplied container image —— 验证所提供容器镜像上的签名
底层实际执行逻辑位于 cmd/cosign/cli/verify/verify.go 的VerifyCommand.Exec方法,它负责组装cosign.CheckOpts校验选项、加载验证器与信任材料,并逐镜像执行校验。从源码可以看到,命令要求至少传入一个镜像参数(Args: cobra.MinimumNArgs(1),见 cmd/cosign/cli/verify.go),即不带任何参数运行时会直接报错退出。
二、命令语法
cosign verify [flags]cosign verify的完整调用形态为:
cosign verify --key <key path>|<key url>|<kms uri> <image uri> [<image uri> ...]其中:
--key指定用于验证的公钥来源(文件路径、URL、KMS URI 或 Kubernetes Secret);<image uri>为一个或多个待验证的镜像引用,支持同时传入多个镜像批量验证。
三、实战示例:从最简单到企业级场景
原文档给出了从基础到复杂的一整套验证示例,下面逐一展开讲解其适用场景与关键点(示例源见 cmd/cosign/cli/verify.go)。
3.1 验证 cosign claims 与签名证书(keyless 默认路径)
# verify cosign claims and signing certificates on the image with the transparency log cosign verify <IMAGE>这是最基础也是最常用的形态:不显式指定--key,走keyless 验证。此时 cosign 会从镜像的签名附件中提取 X.509 证书,将其链到 Fulcio 根信任(或用户通过--trusted-root提供的信任根),并同时核对 Rekor 透明度日志中的条目。从源码看,未指定 key 且不使用安全密钥时,会要求提供证书身份与 OIDC issuer 的匹配条件(Identities()逻辑,见 cmd/cosign/cli/options/certificate.go)。
3.2 批量验证多个镜像
# verify multiple images cosign verify <IMAGE_1> <IMAGE_2> ...Exec中对传入的images []string做循环遍历(cmd/cosign/cli/verify/verify.go),逐个镜像独立执行验证并输出结果。注意:所有镜像必须能被同一组验证条件(同一个 key / 身份 / issuer)通过,否则对应的镜像校验会报错。
3.3 额外校验注解
# additionally verify specified annotations cosign verify -a key1=val1 -a key2=val2 <IMAGE>-a, --annotations(key=value形式,可多次指定)要求签名 payload 中必须包含这些注解。注解校验在 claims 校验阶段完成——CheckOpts.Annotations字段被填充后,ClaimVerifier会对其逐一核对(见 pkg/cosign/verify.go)。
3.4 使用磁盘上的公钥验证(key-based)
# verify image with an on-disk public key cosign verify --key cosign.pub <IMAGE>--key指向本地公钥文件(如cosign.pub,可用cosign generate-key-pair生成)。加载公钥的路径在LoadVerifierFromKeyOrCert中:当keyRef != ""时调用PublicKeyFromKeyRefWithHashAlgo构建验证器(见 cmd/cosign/cli/verify/common.go)。此模式下不再需要 Fulcio 证书,属于"显式公钥验证"路径。
3.5 验证本地保存的镜像(离线)
# verify image with an on-disk signed image from 'cosign save' cosign verify --key cosign.pub --local-image <PATH>--local-image表示<PATH>是通过cosign save保存到本地的镜像目录而非远程仓库引用。此时验证完全离线进行,调用cosign.VerifyLocalImageSignatures(pkg/cosign/verify.go),从本地 OCI layout 中读取签名并校验,不发起任何网络请求。
3.6 使用 TrustedRoot 信任根验证
# verify image with a trusted root cosign verify --trusted-root trusted_root.json <IMAGE>--trusted-root指向一个 Sigstore TrustedRoot JSON 文件,其中包含 Rekor、Fulcio、CT log、TSA 等服务密钥与证书,可在离线/隔离环境中提供完整的信任锚点。源码中通过root.NewTrustedRootFromPath加载(见 cmd/cosign/cli/verify/common.go)。提供了 TrustedRoot 后,cosign 不再从 TUF 仓库在线拉取各服务密钥。
3.7 通过 URL 提供公钥
# verify image with public key provided by URL cosign verify --key https://host.for/[FILE] <IMAGE>--key支持http(s)://URL,cosign 会先下载公钥内容再构建验证器。
3.8 公钥存储在环境变量中
# verify image with a key stored in an environment variable cosign verify --key env://[ENV_VAR] <IMAGE>env://前缀表示从指定环境变量读取公钥内容,适合 CI/CD 流水线中通过 secret 注入密钥的场景。
3.9 KMS 与密钥管理服务集成
# verify image with public key stored in Google Cloud KMS cosign verify --key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY] <IMAGE> # verify image with public key stored in Hashicorp Vault cosign verify --key hashivault://[KEY] <IMAGE> # verify image with public key stored in a Kubernetes secret cosign verify --key k8s://[NAMESPACE]/[KEY] <IMAGE>--key支持多种 KMS 前缀:gcpkms://(Google Cloud KMS)、hashivault://(HashiCorp Vault)、k8s://(Kubernetes Secret)。这些 URI 在PublicKeyFromKeyRefWithHashAlgo中被统一解析,最终返回对应的公钥验证器。
3.10 GitLab 托管公钥
# verify image with public key stored in GitLab with project name cosign verify --key gitlab://[OWNER]/[PROJECT_NAME] <IMAGE> # verify image with public key stored in GitLab with project id cosign verify --key gitlab://[PROJECT_ID] <IMAGE>gitlab://前缀支持按项目名(OWNER/PROJECT_NAME)或项目 ID 两种方式引用存储在 GitLab 中的公钥。
四、参数详解
4.1 verify 专属参数
以下参数完整继承自原文档,并补充了源码中的默认值与行为说明(选项定义见 cmd/cosign/cli/options/verify.go 与 cmd/cosign/cli/options/certificate.go):
| 参数 | 类型/默认值 | 说明 |
|---|---|---|
--allow-certificate-chain | bool(默认 false) | 允许在 v0.3+ 版本 bundle 的验证材料中包含 X.509 证书链。设置后会在 OCI 客户端选项中追加sgbundle.AllowCertificateChain()(见 cmd/cosign/cli/verify/verify.go) |
--allow-http-registry | bool | 是否允许使用 HTTP 协议连接镜像仓库。仅限测试环境使用 |
--allow-insecure-registry | bool | 是否允许不安全的仓库连接(如证书过期或自签名 TLS)。仅限测试环境使用 |
-a, --annotations | strings | 额外的key=value注解对,需与签名中的注解完全匹配 |
--certificate-github-workflow-name | string | 期望的 GitHub OIDC Identity Token 中 workflow 名称 claim |
--certificate-github-workflow-ref | string | 期望的 git ref claim |
--certificate-github-workflow-repository | string | 期望的仓库 claim |
--certificate-github-workflow-sha | string | 期望的 commit SHA claim |
--certificate-github-workflow-trigger | string | 期望的触发事件名(event_name)claim |
--certificate-identity | string | 期望在有效 Fulcio 证书中的身份,支持邮箱、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。keyless 流程必须设置它或--certificate-oidc-issuer-regexp |
--certificate-oidc-issuer-regexp | string | 上述 issuer 的正则表达式替代形式 |
--check-claims | bool(默认 true) | 是否校验签名中发现的 claims |
-h, --help | bool | 查看 verify 帮助 |
--insecure-ignore-sct | bool | 设置为 true 时不检查证书是否内嵌 SCT(证书透明度日志包含证明) |
--insecure-ignore-tlog | bool | 跳过透明度日志验证,用于签名未上传至透明日志的场景。未纳入日志的产物无法被公开验证 |
--k8s-keychain | bool | 使用 Kubernetes keychain 替代默认 keychain(支持 workload identity) |
--key | string | 公钥文件路径、KMS URI 或 Kubernetes Secret |
--local-image | bool | 指定镜像为cosign save保存的本地路径 |
--max-workers | int(默认 10) | 并行执行的最大 worker 数。源码中若设为 0 会直接报错提示设置为大于 0 的值(见 cmd/cosign/cli/verify.go),实际并行节流通过throttler实现(见 pkg/cosign/verify.go) |
-o, --output | string(默认json) | 签名镜像信息的输出格式,可选json或text |
--registry-cacert | string | 连接仓库时使用的 X.509 CA 证书文件(PEM 格式)路径 |
--registry-client-cert | string | mTLS 客户端证书(PEM)路径 |
--registry-client-key | string | mTLS 客户端私钥(PEM)路径,需与--registry-client-cert搭配 |
--registry-password | string | 仓库 basic auth 密码 |
--registry-server-name | string | 作为tls.Config中ServerName使用的 SAN 名,用于校验与仓库的 mTLS 连接 |
--registry-token | string | 仓库 bearer auth token |
--registry-username | string | 仓库 basic auth 用户名 |
--sk | bool | 是否使用硬件安全密钥(如 YubiKey PIV) |
--slot | string(默认signature) | 安全密钥槽位:authentication、signature、card-authentication、key-management |
--trusted-root | string | Sigstore TrustedRoot JSON 文件路径 |
--use-signed-timestamps | bool | 验证 RFC3161 时间戳 |
注意:
--certificate-github-workflow-*系列参数对应 Fulcio 证书中的 GitHub Actions OIDC 扩展字段,用于在 keyless 场景下将验证收紧到"某个特定 workflow 的某次运行",避免任何持有合法 Fulcio 证书的 GitHub 用户都能通过验证。
4.2 从父命令继承的参数
--output-file string log output to a file -t, --timeout duration timeout for commands (default 3m0s) -d, --verbose log debug output--output-file:将日志输出到指定文件;-t, --timeout:命令超时时间,默认 3 分钟。入口处通过context.WithTimeout(cmd.Context(), ro.Timeout)为整个验证流程设置超时(见 cmd/cosign/cli/verify.go);-d, --verbose:输出调试日志。
五、验证流程的源码级解析
理解cosign verify的底层流程有助于准确排查验证失败原因。核心验证逻辑位于 pkg/cosign/verify.go 的verifyInternal(pkg/cosign/verify.go),整体流程如下:
信任材料准备:
SetTrustedMaterial(cmd/cosign/cli/verify/common.go)优先使用--trusted-root提供的 TrustedRoot;否则从 TUF 仓库在线获取。若仅使用公钥验证(--key且跳过 tlog 与时间戳),则无需信任根(verifyOfflineWithKey,见 cmd/cosign/cli/verify/common.go)。构建验证器:
LoadVerifierFromKeyOrCert(cmd/cosign/cli/verify/common.go)按优先级处理——显式--key> 硬件安全密钥--sk> 证书--certificate。不提供三者时SigVerifier为 nil,验证器将从签名携带的证书构建(keyless 路径)。拉取签名:
VerifyImageSignatures(pkg/cosign/verify.go)首先解析镜像 digest,然后从 OCI 仓库的签名 tag 或显式--signature引用拉取签名集合。逐签名验证:
verifySignatures使用--max-workers控制的 worker 池并行处理每个签名(pkg/cosign/verify.go),对每个签名依次执行:- 透明日志核对:验证 Rekor bundle(离线可验),无 bundle 则通过
RekorClient在线查询tlogValidateEntry(pkg/cosign/verify.go); - 证书链验证:证书链到可信根,并执行身份/issuer 策略匹配(
CheckCertificatePolicy,见 pkg/cosign/verify.go)与 SCT 校验(除非--insecure-ignore-sct); - 密码学签名校验:用验证器对签名做实际签名验证;
- claims 校验:
ClaimVerifier(默认SimpleClaimVerifier)检查镜像 digest 与注解是否与签名 payload 一致(cmd/cosign/cli/verify/verify.go); - 证书过期检查:优先使用 Rekor 集成时间或 RFC3161 时间戳时间,否则用当前时间(pkg/cosign/verify.go)。
- 透明日志核对:验证 Rekor bundle(离线可验),无 bundle 则通过
输出结果:全部签名验证成功后,
PrintVerificationHeader打印执行过的检查项清单,PrintVerification按-o指定的格式输出(cmd/cosign/cli/verify/common.go)。
验证成功的标准输出
以默认json格式为例,验证通过后会先打印如下检查清单:
Verification for <IMAGE> -- The following checks were performed on each of these signatures: - The cosign claims were validated - Existence of the claims in the transparency log was verified offline - The signatures were verified against the specified public key随后输出 JSON 数组,包含每个签名对应的Critical(docker 引用与镜像 manifest digest)与Optional字段(含 Subject、Issuer、Bundle 等信息)。使用-o text则会输出人类可读的证书主体、issuer URL、GitHub Workflow 信息以及 payload 明文。
六、keyless 验证的强制约束
从 cmd/cosign/cli/options/certificate.go 可以看到,keyless 验证(即不提供--key、--sk)必须同时满足:
- 提供
--certificate-identity或--certificate-identity-regexp; - 提供
--certificate-oidc-issuer或--certificate-oidc-issuer-regexp。
否则会直接返回错误。这是设计使然——keyless 模式下必须显式声明"谁"(身份)与"从哪里签发"(issuer)才可接受,防止任意持有证书者通过验证。同时,--key与证书身份参数互斥(NOf(...) > 1时报KeyAndIdentityParseError,见 cmd/cosign/cli/verify/verify.go)。
七、安全注意事项
--allow-http-registry、--allow-insecure-registry、--insecure-ignore-sct、--insecure-ignore-tlog等带insecure/allow字样的参数都会削弱验证强度,原文档与源码均明确提示仅限测试环境使用;- 跳过 tlog 验证时,cosign 会输出显式警告:
Skipping tlog verification is an insecure practice that lacks transparency and auditability verification for the %s.(见 cmd/cosign/cli/verify.go 与 cmd/cosign/cli/verify.go); - 对于未上传至透明度日志的签名,
--insecure-ignore-tlog是唯一选择,但这意味着产物无法被第三方公开验证,审计与防抵赖能力随之丧失。
八、与其他命令的配合
cosign verify是 cosign 验证体系的入口命令,与之配套的还包括:
- cosign verify-attestation:验证镜像上的 in-toto attestation(声明性证据),支持 CUE/Rego 策略;
- cosign verify-blob:验证二进制 blob 的签名;
- cosign verify-blob-attestation:验证 blob 的 attestation;
- cosign save:将镜像(含签名)保存到本地,配合
--local-image离线验证; - cosign generate-key-pair:生成用于
--key的公私钥对。
上述命令的入口均注册在 cmd/cosign/cli/verify.go 中,共享同一套CommonVerifyOptions(cmd/cosign/cli/options/verify.go),因此--trusted-root、--max-workers、--insecure-ignore-tlog等参数在全部 verify 系列命令中行为一致。主命令帮助文档见 doc/cosign.md。
九、常见验证失败与排查建议
| 失败现象 | 常见原因 | 排查方向 |
|---|---|---|
no matching signatures | 签名无法通过任一检查项 | 检查 key 是否匹配、身份/issuer 条件是否与证书一致 |
none of the expected identities matched | 证书身份不匹配 | 核对--certificate-identity与证书 SAN、--certificate-oidc-issuer与证书 OIDC issuer 扩展 |
no valid tlog entries found | 透明日志中无对应条目 | 确认签名确实上传过 Rekor,或评估是否使用--insecure-ignore-tlog |
certificate does not include required embedded SCT | 证书缺少 SCT 且未提供 detached SCT | 重新签发带 SCT 的证书,或仅在测试环境使用--insecure-ignore-sct |
expected a signed timestamp to verify an expired certificate | 证书已过期且未提供时间戳 | 使用--use-signed-timestamps或提供带 bundle 的签名材料 |
image tag not found | 镜像引用解析失败 | 核对镜像 URI 与--allow-insecure-registry/--allow-http-registry设置 |
| 本地镜像验证失败 | --local-image路径不是cosign save产物 | 确认目录结构为 OCI layout 且包含签名 |
【免费下载链接】cosignCode signing and transparency for containers and binaries项目地址: https://gitcode.com/GitHub_Trending/co/cosign
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考